@lobehub/editor 4.17.2 → 4.18.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/headless/index.js +12 -2
- package/es/headless.js +3119 -1224
- package/es/index.d.ts +1 -1
- package/es/index.js +13 -13
- package/es/plugins/codeblock/command/index.d.ts +2 -8
- package/es/plugins/codeblock/command/index.js +3 -3
- package/es/plugins/codeblock/command/symbols.d.ts +8 -0
- package/es/plugins/codeblock/command/symbols.js +5 -0
- package/es/plugins/codeblock/index.d.ts +1 -1
- package/es/plugins/codeblock/index.js +1 -1
- package/es/plugins/codeblock/plugin/FacadeShiki.js +1 -1
- package/es/plugins/codeblock/plugin/index.js +1 -1
- package/es/plugins/codemirror-block/command/index.js +1 -1
- package/es/plugins/codemirror-block/node/CodeMirrorNode.js +10 -1
- package/es/plugins/codemirror-block/plugin/index.d.ts +1 -1
- package/es/plugins/file/plugin/index.d.ts +2 -2
- package/es/plugins/file/plugin/index.js +26 -23
- package/es/plugins/image/node/block-image-node.d.ts +1 -0
- package/es/plugins/image/node/block-image-node.js +6 -0
- package/es/plugins/image/node/image-node.d.ts +1 -0
- package/es/plugins/image/node/image-node.js +6 -0
- package/es/plugins/image/plugin/index.d.ts +3 -3
- package/es/plugins/image/plugin/index.js +5 -23
- package/es/plugins/image/react/ReactImagePlugin.js +18 -0
- package/es/plugins/image/react/components/Image.js +1 -1
- package/es/plugins/math/plugin/index.d.ts +1 -1
- package/es/plugins/math/react/components/MathEditor.js +1 -1
- package/es/plugins/math/react/components/MathInline.js +1 -1
- package/es/plugins/mention/plugin/index.d.ts +1 -1
- package/es/plugins/mention/plugin/index.js +1 -1
- package/es/react/hooks/useEditorState/index.js +2 -2
- package/es/renderer/engine/shiki.js +1 -1
- package/es/renderer/nodes/index.js +4 -4
- package/es/renderer/renderers/codeblock.js +1 -0
- package/package.json +1 -1
- package/es/headless/plugins/codeblock.js +0 -82
package/es/headless.js
CHANGED
|
@@ -4,10 +4,10 @@ import { createEmptyHistoryState, registerHistory } from "@lexical/history";
|
|
|
4
4
|
import { $computeTableMapSkipCellCheck, $createTableNodeWithDimensions, $createTableSelection, $deleteTableColumnAtSelection, $deleteTableRowAtSelection, $findTableNode, $insertTableColumnAtSelection, $insertTableRowAtSelection, $isSimpleTable, $isTableCellNode, $isTableNode, $isTableRowNode, $isTableSelection, TableCellNode, TableNode, TableNode as TableNode$1, TableRowNode, registerTableCellUnmergeTransform, registerTablePlugin, registerTableSelectionObserver, setScrollableTablesActive } from "@lexical/table";
|
|
5
5
|
import { get, merge, template, templateSettings } from "es-toolkit/compat";
|
|
6
6
|
import EventEmitter from "eventemitter3";
|
|
7
|
-
import { $applyNodeReplacement, $caretFromPoint, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createRangeSelection, $createTextNode, $getCaretRange, $getCharacterOffsets, $getChildCaret, $getNearestNodeFromDOMNode, $getNodeByKey, $getPreviousSelection, $getRoot, $getSelection, $insertNodes, $isBlockElementNode, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isNodeSelection, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isTextNode, $isTextPointCaret, $nodesOfType, $normalizeSelection__EXPERIMENTAL, $parseSerializedNode, $setSelection, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, DecoratorNode, ElementNode, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, IS_BOLD, IS_CODE, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, ParagraphNode, REDO_COMMAND, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, TEXT_TYPE_TO_FORMAT,
|
|
7
|
+
import { $applyNodeReplacement, $caretFromPoint, $createLineBreakNode, $createNodeSelection, $createParagraphNode, $createRangeSelection, $createTextNode, $getCaretRange, $getCharacterOffsets, $getChildCaret, $getNearestNodeFromDOMNode, $getNodeByKey, $getPreviousSelection, $getRoot, $getSelection, $insertNodes, $isBlockElementNode, $isDecoratorNode, $isElementNode, $isLineBreakNode, $isNodeSelection, $isRangeSelection, $isRootNode, $isRootOrShadowRoot, $isTextNode, $isTextPointCaret, $nodesOfType, $normalizeSelection__EXPERIMENTAL, $parseSerializedNode, $setSelection, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, CONTROLLED_TEXT_INSERTION_COMMAND, COPY_COMMAND, DecoratorNode, ElementNode, FORMAT_TEXT_COMMAND, HISTORIC_TAG, HISTORY_MERGE_TAG, HISTORY_PUSH_TAG, INDENT_CONTENT_COMMAND, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND, INSERT_TAB_COMMAND, IS_BOLD, IS_CODE, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DOWN_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND, PASTE_COMMAND, ParagraphNode, REDO_COMMAND, SELECTION_CHANGE_COMMAND, SELECTION_INSERT_CLIPBOARD_NODES_COMMAND, TEXT_TYPE_TO_FORMAT, TextNode, UNDO_COMMAND, createCommand, createEditor, getNearestEditorFromDOMNode, isHTMLElement, isModifierMatch, resetRandomKey, setDOMUnmanaged } from "lexical";
|
|
8
8
|
import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode, HeadingNode, QuoteNode, registerRichText } from "@lexical/rich-text";
|
|
9
9
|
import createDebug from "debug";
|
|
10
|
-
import { $filter, $findMatchingParent, $getNearestBlockElementAncestorOrThrow, $getNearestNodeOfType, $insertNodeToNearestRoot, CAN_USE_DOM, IS_APPLE as isApple, addClassNamesToElement, calculateZoomLevel, isHTMLAnchorElement, isHTMLElement as isHTMLElement$1, mergeRegister } from "@lexical/utils";
|
|
10
|
+
import { $filter, $findMatchingParent, $getNearestBlockElementAncestorOrThrow, $getNearestNodeOfType, $insertNodeToNearestRoot, $wrapNodeInElement, CAN_USE_DOM, IS_APPLE as isApple, addClassNamesToElement, calculateZoomLevel, isHTMLAnchorElement, isHTMLElement as isHTMLElement$1, mergeRegister } from "@lexical/utils";
|
|
11
11
|
import { $isAtNodeEnd, $setBlocksType } from "@lexical/selection";
|
|
12
12
|
import { registerDragonSupport } from "@lexical/dragon";
|
|
13
13
|
import { $createListItemNode, $createListNode, $insertList, $isListItemNode, $isListNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListItemNode, ListNode, registerList, registerListStrictIndentTransform } from "@lexical/list";
|
|
@@ -17,8 +17,7 @@ import remarkCjkFriendly from "remark-cjk-friendly";
|
|
|
17
17
|
import remarkGfm from "remark-gfm";
|
|
18
18
|
import remarkMath from "remark-math";
|
|
19
19
|
import { $getClipboardDataFromSelection, setLexicalClipboardDataTransfer } from "@lexical/clipboard";
|
|
20
|
-
import { $isCodeHighlightNode, $isCodeNode
|
|
21
|
-
import { bundledLanguagesInfo } from "shiki";
|
|
20
|
+
import { $isCodeHighlightNode, $isCodeNode } from "@lexical/code-core";
|
|
22
21
|
//#region src/locale/index.ts
|
|
23
22
|
var locale_default = {
|
|
24
23
|
block: {
|
|
@@ -594,7 +593,7 @@ const isHotkeyMatch = (event, hotkey) => {
|
|
|
594
593
|
};
|
|
595
594
|
//#endregion
|
|
596
595
|
//#region src/utils/hotkey/registerHotkey.ts
|
|
597
|
-
const logger$
|
|
596
|
+
const logger$9 = createDebugLogger("hotkey");
|
|
598
597
|
const registerHotkey = (hotkey, callback, options = {}) => {
|
|
599
598
|
return (e) => {
|
|
600
599
|
if (!isHotkeyMatch(e, hotkey.keys)) return false;
|
|
@@ -606,7 +605,7 @@ const registerHotkey = (hotkey, callback, options = {}) => {
|
|
|
606
605
|
...keys,
|
|
607
606
|
scopes: hotkey.scopes
|
|
608
607
|
});
|
|
609
|
-
logger$
|
|
608
|
+
logger$9.debug(`⌨️ Hotkey matched: ${hotkey.id} [${hotkey.keys}]`, hotkey);
|
|
610
609
|
return true;
|
|
611
610
|
};
|
|
612
611
|
};
|
|
@@ -1423,7 +1422,7 @@ function registerCommands(editor) {
|
|
|
1423
1422
|
}
|
|
1424
1423
|
//#endregion
|
|
1425
1424
|
//#region src/plugins/common/node/cursor.ts
|
|
1426
|
-
const logger$
|
|
1425
|
+
const logger$8 = createDebugLogger("common", "cursor");
|
|
1427
1426
|
var CardLikeElementNode = class extends ElementNode {
|
|
1428
1427
|
isCardLike() {
|
|
1429
1428
|
return true;
|
|
@@ -1462,7 +1461,7 @@ function registerCursorNode(editor) {
|
|
|
1462
1461
|
const needAddCursor = [];
|
|
1463
1462
|
for (const [kClass, nodeMaps] of mutatedNodes) if (DecoratorNode.prototype.isPrototypeOf(kClass.prototype)) for (const [key, mutation] of nodeMaps) {
|
|
1464
1463
|
const node = $getNodeByKey(key);
|
|
1465
|
-
logger$
|
|
1464
|
+
logger$8.debug("🎭 DecoratorNode mutated:", node?.getType(), mutation, node);
|
|
1466
1465
|
if (mutation === "created" && node?.isInline() && node.getNextSibling() === null) needAddCursor.push(node);
|
|
1467
1466
|
}
|
|
1468
1467
|
if (needAddCursor.length > 0) editor.update(() => {
|
|
@@ -1564,7 +1563,7 @@ function registerCursorNode(editor) {
|
|
|
1564
1563
|
$setSelection(sel);
|
|
1565
1564
|
return true;
|
|
1566
1565
|
} catch (error) {
|
|
1567
|
-
logger$
|
|
1566
|
+
logger$8.error("❌ Cursor selection error:", error);
|
|
1568
1567
|
}
|
|
1569
1568
|
else if ($isCursorNode(focusNode)) try {
|
|
1570
1569
|
const { key: anchorKey, offset: anchorOffset, type: anchorType } = selection.anchor;
|
|
@@ -1575,7 +1574,7 @@ function registerCursorNode(editor) {
|
|
|
1575
1574
|
$setSelection(sel);
|
|
1576
1575
|
return true;
|
|
1577
1576
|
} catch (error) {
|
|
1578
|
-
logger$
|
|
1577
|
+
logger$8.error("❌ Cursor navigation error:", error);
|
|
1579
1578
|
}
|
|
1580
1579
|
return false;
|
|
1581
1580
|
}, COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ARROW_RIGHT_COMMAND, (event) => {
|
|
@@ -1754,7 +1753,7 @@ function charToId(char) {
|
|
|
1754
1753
|
}
|
|
1755
1754
|
//#endregion
|
|
1756
1755
|
//#region src/plugins/litexml/command/index.ts
|
|
1757
|
-
const logger$
|
|
1756
|
+
const logger$7 = createDebugLogger("plugin", "litexml");
|
|
1758
1757
|
function toArrayXml(litexml) {
|
|
1759
1758
|
return Array.isArray(litexml) ? litexml : [litexml];
|
|
1760
1759
|
}
|
|
@@ -1766,7 +1765,7 @@ function tryParseChild(child, editor) {
|
|
|
1766
1765
|
oldNode
|
|
1767
1766
|
};
|
|
1768
1767
|
} catch (error) {
|
|
1769
|
-
logger$
|
|
1768
|
+
logger$7.error("❌ Error parsing child node:", error);
|
|
1770
1769
|
return {
|
|
1771
1770
|
newNode: null,
|
|
1772
1771
|
oldNode: null
|
|
@@ -1875,12 +1874,12 @@ function registerLiteXMLCommand(editor, dataSource) {
|
|
|
1875
1874
|
delay: true
|
|
1876
1875
|
}, dataSource);
|
|
1877
1876
|
break;
|
|
1878
|
-
default: logger$
|
|
1877
|
+
default: logger$7.warn(`⚠️ Unknown action type: ${action}`);
|
|
1879
1878
|
}
|
|
1880
1879
|
});
|
|
1881
1880
|
return false;
|
|
1882
1881
|
} catch (error) {
|
|
1883
|
-
logger$
|
|
1882
|
+
logger$7.error("❌ Error processing LITEXML_MODIFY_COMMAND:", error);
|
|
1884
1883
|
return false;
|
|
1885
1884
|
}
|
|
1886
1885
|
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(LITEXML_APPLY_COMMAND, (payload) => {
|
|
@@ -1905,9 +1904,9 @@ function handleModify(editor, dataSource, arrayXml, delay) {
|
|
|
1905
1904
|
try {
|
|
1906
1905
|
const { oldNode, newNode } = tryParseChild(child, editor);
|
|
1907
1906
|
if (oldNode && newNode) handleReplaceForApplyDelay(oldNode, newNode, modifyBlockNodes, diffNodeMap, editor);
|
|
1908
|
-
else logger$
|
|
1907
|
+
else logger$7.warn(`⚠️ Node with key ${child.id} not found for diffing.`);
|
|
1909
1908
|
} catch (error) {
|
|
1910
|
-
logger$
|
|
1909
|
+
logger$7.error("❌ Error replacing node:", error);
|
|
1911
1910
|
}
|
|
1912
1911
|
});
|
|
1913
1912
|
});
|
|
@@ -1931,7 +1930,7 @@ function handleModify(editor, dataSource, arrayXml, delay) {
|
|
|
1931
1930
|
} else prevNode = prevNode.insertAfter(newNode);
|
|
1932
1931
|
else $insertNodes([newNode]);
|
|
1933
1932
|
} catch (error) {
|
|
1934
|
-
logger$
|
|
1933
|
+
logger$7.error("❌ Error replacing node:", error);
|
|
1935
1934
|
}
|
|
1936
1935
|
});
|
|
1937
1936
|
});
|
|
@@ -2068,7 +2067,7 @@ function handleInsert(editor, payload, dataSource) {
|
|
|
2068
2067
|
});
|
|
2069
2068
|
}
|
|
2070
2069
|
} catch (error) {
|
|
2071
|
-
logger$
|
|
2070
|
+
logger$7.error("❌ Error inserting node:", error);
|
|
2072
2071
|
}
|
|
2073
2072
|
});
|
|
2074
2073
|
}
|
|
@@ -2164,6 +2163,39 @@ function registerLiteXMLDiffCommand(editor) {
|
|
|
2164
2163
|
* Service ID for Node service
|
|
2165
2164
|
*/
|
|
2166
2165
|
const INodeService = genServiceId("INodeService");
|
|
2166
|
+
/**
|
|
2167
|
+
* Default implementation of INodeService
|
|
2168
|
+
*/
|
|
2169
|
+
var NodeService = class {
|
|
2170
|
+
constructor() {
|
|
2171
|
+
this.processNodeTreeHandlers = [];
|
|
2172
|
+
}
|
|
2173
|
+
registerProcessNodeTree(process) {
|
|
2174
|
+
this.processNodeTreeHandlers.push(process);
|
|
2175
|
+
}
|
|
2176
|
+
processNodeTree(inode) {
|
|
2177
|
+
for (const handler of this.processNodeTreeHandlers) handler(inode);
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
//#endregion
|
|
2181
|
+
//#region src/plugins/inode/plugin/index.ts
|
|
2182
|
+
/**
|
|
2183
|
+
* LitexmlPlugin - A plugin that provides XML-based data source support
|
|
2184
|
+
* Allows converting between Lexical editor state and XML format
|
|
2185
|
+
*/
|
|
2186
|
+
const INodePlugin = class extends KernelPlugin {
|
|
2187
|
+
static {
|
|
2188
|
+
this.pluginName = "INodePlugin";
|
|
2189
|
+
}
|
|
2190
|
+
constructor(kernel, config) {
|
|
2191
|
+
super();
|
|
2192
|
+
this.kernel = kernel;
|
|
2193
|
+
this.config = config;
|
|
2194
|
+
const nodeService = new NodeService();
|
|
2195
|
+
kernel.registerService(INodeService, nodeService);
|
|
2196
|
+
}
|
|
2197
|
+
onInit() {}
|
|
2198
|
+
};
|
|
2167
2199
|
//#endregion
|
|
2168
2200
|
//#region src/plugins/litexml/service/litexml-service.ts
|
|
2169
2201
|
/**
|
|
@@ -2206,7 +2238,7 @@ var LitexmlService = class {
|
|
|
2206
2238
|
};
|
|
2207
2239
|
//#endregion
|
|
2208
2240
|
//#region src/plugins/litexml/data-source/litexml-data-source.ts
|
|
2209
|
-
const logger$
|
|
2241
|
+
const logger$6 = createDebugLogger("plugin", "litexml");
|
|
2210
2242
|
var IXmlWriterContext = class {
|
|
2211
2243
|
createXmlNode(tagName, attributes, textContent) {
|
|
2212
2244
|
return {
|
|
@@ -2234,7 +2266,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
2234
2266
|
const xml = this.parseXMLString(litexml);
|
|
2235
2267
|
const inode = this.xmlToLexical(xml);
|
|
2236
2268
|
this.getService?.(INodeService)?.processNodeTree(inode);
|
|
2237
|
-
logger$
|
|
2269
|
+
logger$6.debug("Parsed XML to Lexical State:", inode);
|
|
2238
2270
|
return inode;
|
|
2239
2271
|
}
|
|
2240
2272
|
/**
|
|
@@ -2255,7 +2287,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
2255
2287
|
});
|
|
2256
2288
|
editor.setEditorState(newState);
|
|
2257
2289
|
} catch (error) {
|
|
2258
|
-
logger$
|
|
2290
|
+
logger$6.error("Failed to parse XML:", error);
|
|
2259
2291
|
throw error;
|
|
2260
2292
|
}
|
|
2261
2293
|
}
|
|
@@ -2290,7 +2322,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
2290
2322
|
return this.lexicalToXML(rootNode);
|
|
2291
2323
|
});
|
|
2292
2324
|
} catch (error) {
|
|
2293
|
-
logger$
|
|
2325
|
+
logger$6.error("Failed to export to XML:", error);
|
|
2294
2326
|
throw error;
|
|
2295
2327
|
}
|
|
2296
2328
|
}
|
|
@@ -2481,7 +2513,7 @@ var LitexmlDataSource = class extends DataSource {
|
|
|
2481
2513
|
};
|
|
2482
2514
|
//#endregion
|
|
2483
2515
|
//#region src/plugins/markdown/utils/logger.ts
|
|
2484
|
-
const logger$
|
|
2516
|
+
const logger$5 = createDebugLogger("plugin", "markdown");
|
|
2485
2517
|
//#endregion
|
|
2486
2518
|
//#region src/plugins/markdown/data-source/markdown/parse.ts
|
|
2487
2519
|
const selfClosingHtmlTags = new Set([
|
|
@@ -2574,7 +2606,7 @@ function convertMdastToLexical(node, index, ctx, markdownReaders = {}, parentTyp
|
|
|
2574
2606
|
const top = ctx.pop();
|
|
2575
2607
|
htmlStack.pop();
|
|
2576
2608
|
if (top?.tag !== tag) {
|
|
2577
|
-
logger$
|
|
2609
|
+
logger$5.warn("HTML tag mismatch:", tag);
|
|
2578
2610
|
ret.push(...top?.children || []);
|
|
2579
2611
|
return ret;
|
|
2580
2612
|
}
|
|
@@ -2664,7 +2696,7 @@ function registerDefaultReaders(markdownReaders) {
|
|
|
2664
2696
|
}
|
|
2665
2697
|
function parseMarkdownToLexical(markdown, markdownReaders = {}) {
|
|
2666
2698
|
const ast = remark().use(remarkCjkFriendly).use(remarkMath).use([[remarkGfm, { singleTilde: false }]]).parse(markdown);
|
|
2667
|
-
logger$
|
|
2699
|
+
logger$5.debug("Parsed MDAST:", ast);
|
|
2668
2700
|
const ctx = new MarkdownContext(ast, markdown);
|
|
2669
2701
|
registerDefaultReaders(markdownReaders);
|
|
2670
2702
|
return convertMdastToLexical(ast, 0, ctx, markdownReaders);
|
|
@@ -2812,7 +2844,7 @@ function insertIRootNode(editor, root, selection) {
|
|
|
2812
2844
|
}
|
|
2813
2845
|
//#endregion
|
|
2814
2846
|
//#region src/plugins/markdown/command/index.ts
|
|
2815
|
-
const logger$
|
|
2847
|
+
const logger$4 = createDebugLogger("plugin", "markdown");
|
|
2816
2848
|
const INSERT_MARKDOWN_COMMAND = createCommand("INSERT_MARKDOWN_COMMAND");
|
|
2817
2849
|
const GET_MARKDOWN_SELECTION_COMMAND = createCommand("GET_MARKDOWN_SELECTION_COMMAND");
|
|
2818
2850
|
function restoreToEntry(editor, entry) {
|
|
@@ -2826,7 +2858,7 @@ const getLineNumber = (content, charIndex) => {
|
|
|
2826
2858
|
function registerMarkdownCommand(editor, kernel, service) {
|
|
2827
2859
|
return mergeRegister(editor.registerCommand(INSERT_MARKDOWN_COMMAND, (payload) => {
|
|
2828
2860
|
const { markdown } = payload;
|
|
2829
|
-
logger$
|
|
2861
|
+
logger$4.debug("INSERT_MARKDOWN_COMMAND payload:", payload);
|
|
2830
2862
|
restoreToEntry(editor, payload.historyState);
|
|
2831
2863
|
setTimeout(() => {
|
|
2832
2864
|
editor.update(() => {
|
|
@@ -2834,11 +2866,11 @@ function registerMarkdownCommand(editor, kernel, service) {
|
|
|
2834
2866
|
const root = parseMarkdownToLexical(markdown, service.markdownReaders);
|
|
2835
2867
|
const selection = $getSelection();
|
|
2836
2868
|
const nodes = $generateNodesFromSerializedNodes(root.children);
|
|
2837
|
-
logger$
|
|
2869
|
+
logger$4.debug("INSERT_MARKDOWN_COMMAND nodes:", nodes);
|
|
2838
2870
|
$insertGeneratedNodes(editor, nodes, selection);
|
|
2839
2871
|
return true;
|
|
2840
2872
|
} catch (error) {
|
|
2841
|
-
logger$
|
|
2873
|
+
logger$4.error("Failed to handle markdown paste:", error);
|
|
2842
2874
|
}
|
|
2843
2875
|
}, { tag: HISTORY_PUSH_TAG });
|
|
2844
2876
|
}, 0);
|
|
@@ -2869,8 +2901,8 @@ function registerMarkdownCommand(editor, kernel, service) {
|
|
|
2869
2901
|
const startLine = getLineNumber(markdownContent, startIndex);
|
|
2870
2902
|
const endLine = getLineNumber(markdownContent, endIndex);
|
|
2871
2903
|
payload.onResult(startLine, endLine);
|
|
2872
|
-
logger$
|
|
2873
|
-
logger$
|
|
2904
|
+
logger$4.debug("GET_MARKDOWN_SELECTION_COMMAND markdownContent:", markdownContent);
|
|
2905
|
+
logger$4.debug("GET_MARKDOWN_SELECTION_COMMAND startLine:", startLine, "endLine:", endLine);
|
|
2874
2906
|
return markdownContent;
|
|
2875
2907
|
} });
|
|
2876
2908
|
}
|
|
@@ -2930,7 +2962,7 @@ var MarkdownDataSource = class extends DataSource {
|
|
|
2930
2962
|
}).processSync(markdown);
|
|
2931
2963
|
return String(result);
|
|
2932
2964
|
} catch (error) {
|
|
2933
|
-
logger$
|
|
2965
|
+
logger$5.error("Failed to format markdown:", error);
|
|
2934
2966
|
return markdown;
|
|
2935
2967
|
}
|
|
2936
2968
|
}
|
|
@@ -2943,7 +2975,7 @@ var MarkdownDataSource = class extends DataSource {
|
|
|
2943
2975
|
read(editor, data) {
|
|
2944
2976
|
const inode = { root: parseMarkdownToLexical(data, this.markdownService.markdownReaders) };
|
|
2945
2977
|
this.getService?.(INodeService)?.processNodeTree(inode);
|
|
2946
|
-
logger$
|
|
2978
|
+
logger$5.debug("Parsed Lexical State:", inode);
|
|
2947
2979
|
editor.setEditorState(editor.parseEditorState(inode));
|
|
2948
2980
|
}
|
|
2949
2981
|
write(editor, options) {
|
|
@@ -4116,6 +4148,15 @@ const DEFAULT_OPTIONS = {
|
|
|
4116
4148
|
lineNumbers: false,
|
|
4117
4149
|
tabSize: 2
|
|
4118
4150
|
};
|
|
4151
|
+
const extractSerializedCodeText$1 = (children) => children.map((child) => {
|
|
4152
|
+
if (!child || typeof child !== "object") return "";
|
|
4153
|
+
const record = child;
|
|
4154
|
+
if (record.type === "linebreak") return "\n";
|
|
4155
|
+
if (record.type === "tab") return " ";
|
|
4156
|
+
if (typeof record.text === "string") return record.text;
|
|
4157
|
+
if (Array.isArray(record.children)) return extractSerializedCodeText$1(record.children);
|
|
4158
|
+
return "";
|
|
4159
|
+
}).join("");
|
|
4119
4160
|
function hasChildDOMNodeTag(node, tagName) {
|
|
4120
4161
|
for (const child of node.childNodes) {
|
|
4121
4162
|
if (isHTMLElement(child) && child.tagName === tagName) return true;
|
|
@@ -4138,7 +4179,7 @@ var CodeMirrorNode = class CodeMirrorNode extends DecoratorNode {
|
|
|
4138
4179
|
}
|
|
4139
4180
|
static importJSON(serializedNode) {
|
|
4140
4181
|
let code = serializedNode.code;
|
|
4141
|
-
if ("children" in serializedNode) code = serializedNode.children
|
|
4182
|
+
if ("children" in serializedNode && Array.isArray(serializedNode.children)) code = extractSerializedCodeText$1(serializedNode.children);
|
|
4142
4183
|
return $createCodeMirrorNode(serializedNode.language, code, serializedNode.codeTheme, serializedNode.options).updateFromJSON(serializedNode);
|
|
4143
4184
|
}
|
|
4144
4185
|
static importDOM() {
|
|
@@ -4301,6 +4342,9 @@ function $convertPreElement(domNode) {
|
|
|
4301
4342
|
const codeTheme = domNode.getAttribute(THEME_DATA_ATTRIBUTE) || "";
|
|
4302
4343
|
return { node: $createCodeMirrorNode(language || "plain", domNode.textContent || "", codeTheme) };
|
|
4303
4344
|
}
|
|
4345
|
+
function $isCodeMirrorNode(node) {
|
|
4346
|
+
return node.getType() === CodeMirrorNode.getType();
|
|
4347
|
+
}
|
|
4304
4348
|
function convertCodeNoop() {
|
|
4305
4349
|
return { node: null };
|
|
4306
4350
|
}
|
|
@@ -4364,7 +4408,7 @@ function runPasteHandlers(ctx, handlers) {
|
|
|
4364
4408
|
}
|
|
4365
4409
|
//#endregion
|
|
4366
4410
|
//#region src/plugins/common/plugin/register.ts
|
|
4367
|
-
const logger$
|
|
4411
|
+
const logger$3 = createDebugLogger("plugin", "common");
|
|
4368
4412
|
function resolveElement(element, isBackward, focusOffset) {
|
|
4369
4413
|
const parent = element.getParent();
|
|
4370
4414
|
let offset = focusOffset;
|
|
@@ -4465,7 +4509,7 @@ function registerRichKeydown(editor, kernel, options) {
|
|
|
4465
4509
|
selection.insertText(text);
|
|
4466
4510
|
});
|
|
4467
4511
|
} catch (error) {
|
|
4468
|
-
logger$
|
|
4512
|
+
logger$3.error("❌ Failed to paste as plain text:", error);
|
|
4469
4513
|
}
|
|
4470
4514
|
}, {
|
|
4471
4515
|
enabled: enableHotkey,
|
|
@@ -4974,12 +5018,12 @@ const CommonPlugin = class extends KernelPlugin {
|
|
|
4974
5018
|
};
|
|
4975
5019
|
//#endregion
|
|
4976
5020
|
//#region src/plugins/code/node/code.ts
|
|
4977
|
-
var CodeNode
|
|
5021
|
+
var CodeNode = class CodeNode extends CardLikeElementNode {
|
|
4978
5022
|
static getType() {
|
|
4979
5023
|
return "codeInline";
|
|
4980
5024
|
}
|
|
4981
5025
|
static clone(node) {
|
|
4982
|
-
return new CodeNode
|
|
5026
|
+
return new CodeNode(node.__key);
|
|
4983
5027
|
}
|
|
4984
5028
|
static importJSON(serializedNode) {
|
|
4985
5029
|
return $createCodeNode().updateFromJSON(serializedNode);
|
|
@@ -5021,14 +5065,14 @@ var CodeNode$1 = class CodeNode$1 extends CardLikeElementNode {
|
|
|
5021
5065
|
}
|
|
5022
5066
|
};
|
|
5023
5067
|
function $createCodeNode(textContent) {
|
|
5024
|
-
const codeNode = $applyNodeReplacement(new CodeNode
|
|
5068
|
+
const codeNode = $applyNodeReplacement(new CodeNode());
|
|
5025
5069
|
const cursorNode = $createCursorNode();
|
|
5026
5070
|
codeNode.append(cursorNode);
|
|
5027
5071
|
if (textContent) codeNode.append($createTextNode(textContent));
|
|
5028
5072
|
return codeNode;
|
|
5029
5073
|
}
|
|
5030
5074
|
function $isCodeInlineNode(node) {
|
|
5031
|
-
return node instanceof CodeNode
|
|
5075
|
+
return node instanceof CodeNode;
|
|
5032
5076
|
}
|
|
5033
5077
|
function getCodeInlineNode(node) {
|
|
5034
5078
|
if ($isCursorNode(node)) {
|
|
@@ -5071,7 +5115,7 @@ function registerCodeInlineCommand(editor) {
|
|
|
5071
5115
|
function registerCodeInline(editor, kernel, options) {
|
|
5072
5116
|
const { enableHotkey = true } = options || {};
|
|
5073
5117
|
return mergeRegister(editor.registerUpdateListener(({ mutatedNodes }) => {
|
|
5074
|
-
const keys = (mutatedNodes?.get(CodeNode
|
|
5118
|
+
const keys = (mutatedNodes?.get(CodeNode))?.keys() || [];
|
|
5075
5119
|
const needAddBefore = /* @__PURE__ */ new Set();
|
|
5076
5120
|
editor.getEditorState().read(() => {
|
|
5077
5121
|
for (const key of keys) {
|
|
@@ -5101,7 +5145,7 @@ const CodePlugin = class extends KernelPlugin {
|
|
|
5101
5145
|
super();
|
|
5102
5146
|
this.kernel = kernel;
|
|
5103
5147
|
this.config = config;
|
|
5104
|
-
kernel.registerNodes([CodeNode
|
|
5148
|
+
kernel.registerNodes([CodeNode]);
|
|
5105
5149
|
kernel.registerThemes({ codeInline: config?.theme || "editor-code" });
|
|
5106
5150
|
}
|
|
5107
5151
|
onInit(editor) {
|
|
@@ -5116,23 +5160,23 @@ const CodePlugin = class extends KernelPlugin {
|
|
|
5116
5160
|
registerLiteXml() {
|
|
5117
5161
|
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
5118
5162
|
if (!litexmlService) return;
|
|
5119
|
-
litexmlService.registerXMLWriter(CodeNode
|
|
5163
|
+
litexmlService.registerXMLWriter(CodeNode.getType(), (node, ctx) => {
|
|
5120
5164
|
return ctx.createXmlNode("codeInline", {});
|
|
5121
5165
|
});
|
|
5122
5166
|
litexmlService.registerXMLReader("codeInline", (xmlElement, children) => {
|
|
5123
|
-
return INodeHelper.createElementNode(CodeNode
|
|
5167
|
+
return INodeHelper.createElementNode(CodeNode.getType(), { children });
|
|
5124
5168
|
});
|
|
5125
5169
|
}
|
|
5126
5170
|
registerMarkdown() {
|
|
5127
5171
|
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
5128
5172
|
if (!markdownService) return;
|
|
5129
|
-
markdownService.registerMarkdownWriter(CodeNode
|
|
5173
|
+
markdownService.registerMarkdownWriter(CodeNode.getType(), (ctx, node) => {
|
|
5130
5174
|
ctx.appendLine(`\`${node.getTextContent().replaceAll("", "")}\``);
|
|
5131
5175
|
return true;
|
|
5132
5176
|
});
|
|
5133
5177
|
markdownService.registerMarkdownShortCuts([{
|
|
5134
5178
|
process: (selection) => {
|
|
5135
|
-
if (selection.getNodes().some((node) => node.getType() === CodeNode
|
|
5179
|
+
if (selection.getNodes().some((node) => node.getType() === CodeNode.getType())) return false;
|
|
5136
5180
|
const text = selection.getTextContent();
|
|
5137
5181
|
selection.removeText();
|
|
5138
5182
|
selection.insertNodes([$createCodeNode(text), $createCursorNode()]);
|
|
@@ -5154,1249 +5198,3174 @@ const CodePlugin = class extends KernelPlugin {
|
|
|
5154
5198
|
}
|
|
5155
5199
|
};
|
|
5156
5200
|
//#endregion
|
|
5157
|
-
//#region src/plugins/
|
|
5158
|
-
|
|
5159
|
-
static getType() {
|
|
5160
|
-
return "horizontalrule";
|
|
5161
|
-
}
|
|
5162
|
-
static clone(node) {
|
|
5163
|
-
return new HorizontalRuleNode(node.__key);
|
|
5164
|
-
}
|
|
5165
|
-
static importJSON(serializedNode) {
|
|
5166
|
-
return $createHorizontalRuleNode().updateFromJSON(serializedNode);
|
|
5167
|
-
}
|
|
5168
|
-
static importDOM() {
|
|
5169
|
-
return { hr: () => ({
|
|
5170
|
-
conversion: $convertHorizontalRuleElement,
|
|
5171
|
-
priority: 0
|
|
5172
|
-
}) };
|
|
5173
|
-
}
|
|
5174
|
-
exportDOM() {
|
|
5175
|
-
return { element: document.createElement("hr") };
|
|
5176
|
-
}
|
|
5177
|
-
createDOM(config) {
|
|
5178
|
-
const element = document.createElement("div");
|
|
5179
|
-
addClassNamesToElement(element, config.theme.hr);
|
|
5180
|
-
return element;
|
|
5181
|
-
}
|
|
5182
|
-
getTextContent() {
|
|
5183
|
-
return "\n";
|
|
5184
|
-
}
|
|
5185
|
-
isInline() {
|
|
5186
|
-
return false;
|
|
5187
|
-
}
|
|
5188
|
-
updateDOM() {
|
|
5189
|
-
return false;
|
|
5190
|
-
}
|
|
5191
|
-
decorate(editor) {
|
|
5192
|
-
const decorator = getKernelFromEditor(editor)?.getDecorator("horizontalrule");
|
|
5193
|
-
if (!decorator) return null;
|
|
5194
|
-
if (typeof decorator === "function") return decorator(this, editor);
|
|
5195
|
-
return {
|
|
5196
|
-
queryDOM: decorator.queryDOM,
|
|
5197
|
-
render: decorator.render(this, editor)
|
|
5198
|
-
};
|
|
5199
|
-
}
|
|
5200
|
-
};
|
|
5201
|
-
function $createHorizontalRuleNode() {
|
|
5202
|
-
return $applyNodeReplacement(new HorizontalRuleNode());
|
|
5203
|
-
}
|
|
5204
|
-
function $convertHorizontalRuleElement() {
|
|
5205
|
-
return { node: $createHorizontalRuleNode() };
|
|
5206
|
-
}
|
|
5207
|
-
function $isHorizontalRuleNode(node) {
|
|
5208
|
-
return node.getType() === HorizontalRuleNode.getType();
|
|
5209
|
-
}
|
|
5201
|
+
//#region src/plugins/codeblock/command/symbols.ts
|
|
5202
|
+
const UPDATE_CODEBLOCK_LANG = createCommand("UPDATE_CODEBLOCK_LANG");
|
|
5210
5203
|
//#endregion
|
|
5211
|
-
//#region src/plugins/
|
|
5212
|
-
const
|
|
5213
|
-
|
|
5214
|
-
|
|
5204
|
+
//#region src/plugins/codemirror-block/command/index.ts
|
|
5205
|
+
const INSERT_CODEMIRROR_COMMAND = createCommand("INSERT_CODEMIRROR_COMMAND");
|
|
5206
|
+
const SELECT_BEFORE_CODEMIRROR_COMMAND = createCommand("SELECT_BEFORE_CODEMIRROR_COMMAND");
|
|
5207
|
+
const SELECT_AFTER_CODEMIRROR_COMMAND = createCommand("SELECT_AFTER_CODEMIRROR_COMMAND");
|
|
5208
|
+
function registerCodeMirrorCommand(editor) {
|
|
5209
|
+
return mergeRegister(editor.registerCommand(INSERT_CODEMIRROR_COMMAND, () => {
|
|
5215
5210
|
editor.update(() => {
|
|
5216
|
-
$
|
|
5211
|
+
const codeMirrorNode = $createCodeMirrorNode("", "");
|
|
5212
|
+
$insertNodes([codeMirrorNode]);
|
|
5213
|
+
const selection = $createNodeSelection();
|
|
5214
|
+
selection.add(codeMirrorNode.getKey());
|
|
5215
|
+
$setSelection(selection);
|
|
5217
5216
|
});
|
|
5218
5217
|
return true;
|
|
5219
|
-
}, COMMAND_PRIORITY_EDITOR)
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
this.kernel = kernel;
|
|
5230
|
-
kernel.registerNodes([HorizontalRuleNode]);
|
|
5231
|
-
kernel.registerThemes({ hr: config?.theme || "" });
|
|
5232
|
-
this.registerDecorator(kernel, "horizontalrule", (node, editor) => {
|
|
5233
|
-
return config?.decorator ? config.decorator(node, editor) : null;
|
|
5234
|
-
});
|
|
5235
|
-
}
|
|
5236
|
-
onInit(editor) {
|
|
5237
|
-
this.register(registerHorizontalRuleCommand(editor));
|
|
5238
|
-
this.registerMarkdown();
|
|
5239
|
-
this.registerLiteXml();
|
|
5240
|
-
}
|
|
5241
|
-
registerLiteXml() {
|
|
5242
|
-
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
5243
|
-
if (!litexmlService) return;
|
|
5244
|
-
litexmlService.registerXMLWriter(HorizontalRuleNode.getType(), (node, ctx) => {
|
|
5245
|
-
if ($isHorizontalRuleNode(node)) return ctx.createXmlNode("hr", {});
|
|
5218
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(UPDATE_CODEBLOCK_LANG, (payload) => {
|
|
5219
|
+
const codeMirrorNode = editor.getEditorState().read(() => {
|
|
5220
|
+
const selection = $getSelection();
|
|
5221
|
+
if ($isRangeSelection(selection)) if (selection.isCollapsed()) return $findMatchingParent(selection.anchor.getNode(), $isCodeMirrorNode);
|
|
5222
|
+
else {
|
|
5223
|
+
const anchor = $findMatchingParent(selection.anchor.getNode(), $isCodeMirrorNode);
|
|
5224
|
+
const focus = $findMatchingParent(selection.focus.getNode(), $isCodeMirrorNode);
|
|
5225
|
+
if (anchor && focus && anchor === focus) return anchor;
|
|
5226
|
+
return null;
|
|
5227
|
+
}
|
|
5246
5228
|
return false;
|
|
5247
5229
|
});
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownShortCut({
|
|
5254
|
-
regExp: /^(---|\*\*\*|___)$/,
|
|
5255
|
-
replace: (parentNode, _1, _2, isImport) => {
|
|
5256
|
-
const line = $createHorizontalRuleNode();
|
|
5257
|
-
if (isImport || parentNode.getNextSibling()) parentNode.replace(line);
|
|
5258
|
-
else parentNode.insertBefore(line);
|
|
5259
|
-
line.selectNext();
|
|
5260
|
-
},
|
|
5261
|
-
trigger: "enter",
|
|
5262
|
-
type: "element"
|
|
5263
|
-
});
|
|
5264
|
-
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(HorizontalRuleNode.getType(), (ctx, node) => {
|
|
5265
|
-
if ($isHorizontalRuleNode(node)) ctx.appendLine("---\n\n");
|
|
5266
|
-
});
|
|
5267
|
-
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("thematicBreak", () => {
|
|
5268
|
-
return INodeHelper.createElementNode("horizontalrule", {});
|
|
5230
|
+
if (!codeMirrorNode) return false;
|
|
5231
|
+
queueMicrotask(() => {
|
|
5232
|
+
editor.update(() => {
|
|
5233
|
+
if ($isCodeMirrorNode(codeMirrorNode)) codeMirrorNode.setLang(payload.lang);
|
|
5234
|
+
});
|
|
5269
5235
|
});
|
|
5270
|
-
|
|
5271
|
-
}
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
const
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
"tel:"
|
|
5281
|
-
]);
|
|
5282
|
-
const HOVER_LINK_COMMAND = createCommand("HOVER_LINK_COMMAND");
|
|
5283
|
-
const HOVER_OUT_LINK_COMMAND = createCommand("HOVER_OUT_LINK_COMMAND");
|
|
5284
|
-
/** @noInheritDoc */
|
|
5285
|
-
var LinkNode = class LinkNode extends ElementNode {
|
|
5286
|
-
static getType() {
|
|
5287
|
-
return "link";
|
|
5288
|
-
}
|
|
5289
|
-
static clone(node) {
|
|
5290
|
-
return new LinkNode(node.__url, {
|
|
5291
|
-
rel: node.__rel,
|
|
5292
|
-
target: node.__target,
|
|
5293
|
-
title: node.__title
|
|
5294
|
-
}, node.__key);
|
|
5295
|
-
}
|
|
5296
|
-
constructor(url = "", attributes = {}, key) {
|
|
5297
|
-
super(key);
|
|
5298
|
-
const { target = null, rel = null, title = null } = attributes;
|
|
5299
|
-
this.__url = url;
|
|
5300
|
-
this.__target = target;
|
|
5301
|
-
this.__rel = rel;
|
|
5302
|
-
this.__title = title;
|
|
5303
|
-
}
|
|
5304
|
-
createDOM(config, editor) {
|
|
5305
|
-
logger.debug("🔍 config", config);
|
|
5306
|
-
const element = document.createElement("a");
|
|
5307
|
-
this.updateLinkDOM(null, element, config);
|
|
5308
|
-
addClassNamesToElement(element, config.theme.link);
|
|
5309
|
-
element.addEventListener("mouseenter", (event) => {
|
|
5310
|
-
if (event.target instanceof HTMLElement) {
|
|
5311
|
-
event.target.classList.add("hover");
|
|
5312
|
-
editor.dispatchCommand(HOVER_LINK_COMMAND, {
|
|
5313
|
-
event,
|
|
5314
|
-
linkNode: this
|
|
5315
|
-
});
|
|
5316
|
-
}
|
|
5236
|
+
return true;
|
|
5237
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(SELECT_BEFORE_CODEMIRROR_COMMAND, (payload) => {
|
|
5238
|
+
editor.update(() => {
|
|
5239
|
+
const node = $getNodeByKey(payload.key);
|
|
5240
|
+
if (!node) return;
|
|
5241
|
+
const prevNode = node.getPreviousSibling();
|
|
5242
|
+
const sel = prevNode?.selectEnd();
|
|
5243
|
+
console.info("SELECT_BEFORE_CODEMIRROR_COMMAND", prevNode, sel);
|
|
5244
|
+
if (sel) $setSelection(sel);
|
|
5245
|
+
editor.focus();
|
|
5317
5246
|
});
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5247
|
+
return false;
|
|
5248
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(SELECT_AFTER_CODEMIRROR_COMMAND, (payload) => {
|
|
5249
|
+
editor.update(() => {
|
|
5250
|
+
const node = $getNodeByKey(payload.key);
|
|
5251
|
+
if (!node) return;
|
|
5252
|
+
const selection = node.getNextSibling()?.selectStart();
|
|
5253
|
+
if (selection) $setSelection(selection);
|
|
5254
|
+
else {
|
|
5255
|
+
const paragraph = $createParagraphNode();
|
|
5256
|
+
node.insertAfter(paragraph);
|
|
5257
|
+
const paragraphSelection = paragraph.selectStart();
|
|
5258
|
+
if (paragraphSelection) $setSelection(paragraphSelection);
|
|
5322
5259
|
}
|
|
5260
|
+
editor.focus();
|
|
5323
5261
|
});
|
|
5324
|
-
return element;
|
|
5325
|
-
}
|
|
5326
|
-
updateLinkDOM(prevNode, anchor, _config) {
|
|
5327
|
-
if (isHTMLAnchorElement(anchor)) {
|
|
5328
|
-
if (!prevNode || prevNode.__url !== this.__url) anchor.href = this.sanitizeUrl(this.__url);
|
|
5329
|
-
for (const attr of [
|
|
5330
|
-
"target",
|
|
5331
|
-
"rel",
|
|
5332
|
-
"title"
|
|
5333
|
-
]) {
|
|
5334
|
-
const key = `__${attr}`;
|
|
5335
|
-
const value = this[key];
|
|
5336
|
-
if (!prevNode || prevNode[key] !== value) if (value) anchor[attr] = value;
|
|
5337
|
-
else anchor.removeAttribute(attr);
|
|
5338
|
-
}
|
|
5339
|
-
}
|
|
5340
|
-
}
|
|
5341
|
-
updateDOM(prevNode, anchor, config) {
|
|
5342
|
-
this.updateLinkDOM(prevNode, anchor, config);
|
|
5343
5262
|
return false;
|
|
5263
|
+
}, COMMAND_PRIORITY_EDITOR));
|
|
5264
|
+
}
|
|
5265
|
+
//#endregion
|
|
5266
|
+
//#region src/codemirror/constants.ts
|
|
5267
|
+
const LANGUAGES = [
|
|
5268
|
+
{
|
|
5269
|
+
ext: ["agda"],
|
|
5270
|
+
name: "Agda",
|
|
5271
|
+
syntax: "text/x-agda",
|
|
5272
|
+
value: "agda"
|
|
5273
|
+
},
|
|
5274
|
+
{
|
|
5275
|
+
ext: ["ets", "arkts"],
|
|
5276
|
+
name: "ArkTS",
|
|
5277
|
+
syntax: "text/x-arkts",
|
|
5278
|
+
value: "arkts"
|
|
5279
|
+
},
|
|
5280
|
+
{
|
|
5281
|
+
ext: ["bash"],
|
|
5282
|
+
name: "Bash",
|
|
5283
|
+
syntax: "shell",
|
|
5284
|
+
value: "bash"
|
|
5285
|
+
},
|
|
5286
|
+
{
|
|
5287
|
+
ext: ["vbs"],
|
|
5288
|
+
name: "Basic",
|
|
5289
|
+
syntax: "vbscript",
|
|
5290
|
+
value: "basic"
|
|
5291
|
+
},
|
|
5292
|
+
{
|
|
5293
|
+
ext: [
|
|
5294
|
+
"c",
|
|
5295
|
+
"h",
|
|
5296
|
+
"ino"
|
|
5297
|
+
],
|
|
5298
|
+
name: "C",
|
|
5299
|
+
syntax: "text/x-csrc",
|
|
5300
|
+
value: "c"
|
|
5301
|
+
},
|
|
5302
|
+
{
|
|
5303
|
+
ext: [
|
|
5304
|
+
"cpp",
|
|
5305
|
+
"c++",
|
|
5306
|
+
"cc",
|
|
5307
|
+
"cxx",
|
|
5308
|
+
"hpp",
|
|
5309
|
+
"h++",
|
|
5310
|
+
"hh",
|
|
5311
|
+
"hxx"
|
|
5312
|
+
],
|
|
5313
|
+
name: "C++",
|
|
5314
|
+
syntax: "text/x-c++src",
|
|
5315
|
+
value: "cpp"
|
|
5316
|
+
},
|
|
5317
|
+
{
|
|
5318
|
+
ext: ["cs"],
|
|
5319
|
+
name: "C#",
|
|
5320
|
+
syntax: "text/x-csharp",
|
|
5321
|
+
value: "csharp"
|
|
5322
|
+
},
|
|
5323
|
+
{
|
|
5324
|
+
ext: ["css"],
|
|
5325
|
+
name: "CSS",
|
|
5326
|
+
syntax: "css",
|
|
5327
|
+
value: "css"
|
|
5328
|
+
},
|
|
5329
|
+
{
|
|
5330
|
+
ext: ["dart"],
|
|
5331
|
+
name: "Dart",
|
|
5332
|
+
syntax: "dart",
|
|
5333
|
+
value: "dart"
|
|
5334
|
+
},
|
|
5335
|
+
{
|
|
5336
|
+
ext: ["diff", "patch"],
|
|
5337
|
+
name: "Diff",
|
|
5338
|
+
syntax: "diff",
|
|
5339
|
+
value: "diff"
|
|
5340
|
+
},
|
|
5341
|
+
{
|
|
5342
|
+
name: "Dockerfile",
|
|
5343
|
+
syntax: "dockerfile",
|
|
5344
|
+
value: "dockerfile"
|
|
5345
|
+
},
|
|
5346
|
+
{
|
|
5347
|
+
ext: ["erl"],
|
|
5348
|
+
name: "Erlang",
|
|
5349
|
+
syntax: "erlang",
|
|
5350
|
+
value: "erlang"
|
|
5351
|
+
},
|
|
5352
|
+
{
|
|
5353
|
+
ext: ["glsl"],
|
|
5354
|
+
name: "Glsl",
|
|
5355
|
+
syntax: "x-shader/x-vertex",
|
|
5356
|
+
value: "glsl"
|
|
5357
|
+
},
|
|
5358
|
+
{
|
|
5359
|
+
name: "Git",
|
|
5360
|
+
syntax: "shell",
|
|
5361
|
+
value: "git"
|
|
5362
|
+
},
|
|
5363
|
+
{
|
|
5364
|
+
ext: ["go"],
|
|
5365
|
+
name: "Go",
|
|
5366
|
+
syntax: "go",
|
|
5367
|
+
value: "go"
|
|
5368
|
+
},
|
|
5369
|
+
{
|
|
5370
|
+
name: "GraphQL",
|
|
5371
|
+
syntax: "graphql",
|
|
5372
|
+
value: "graphql"
|
|
5373
|
+
},
|
|
5374
|
+
{
|
|
5375
|
+
ext: ["groovy", "gradle"],
|
|
5376
|
+
name: "Groovy",
|
|
5377
|
+
syntax: "groovy",
|
|
5378
|
+
value: "groovy"
|
|
5379
|
+
},
|
|
5380
|
+
{
|
|
5381
|
+
ext: [
|
|
5382
|
+
"html",
|
|
5383
|
+
"htm",
|
|
5384
|
+
"handlebars",
|
|
5385
|
+
"hbs"
|
|
5386
|
+
],
|
|
5387
|
+
name: "HTML",
|
|
5388
|
+
syntax: "htmlmixed",
|
|
5389
|
+
value: "html"
|
|
5390
|
+
},
|
|
5391
|
+
{
|
|
5392
|
+
name: "HTTP",
|
|
5393
|
+
syntax: "http",
|
|
5394
|
+
value: "http"
|
|
5395
|
+
},
|
|
5396
|
+
{
|
|
5397
|
+
ext: ["java"],
|
|
5398
|
+
name: "Java",
|
|
5399
|
+
syntax: "text/x-java",
|
|
5400
|
+
value: "java"
|
|
5401
|
+
},
|
|
5402
|
+
{
|
|
5403
|
+
ext: ["js"],
|
|
5404
|
+
name: "JavaScript",
|
|
5405
|
+
syntax: "text/javascript",
|
|
5406
|
+
value: "javascript"
|
|
5407
|
+
},
|
|
5408
|
+
{
|
|
5409
|
+
ext: ["json", "map"],
|
|
5410
|
+
name: "JSON",
|
|
5411
|
+
syntax: "application/json",
|
|
5412
|
+
value: "json"
|
|
5413
|
+
},
|
|
5414
|
+
{
|
|
5415
|
+
ext: ["jsx"],
|
|
5416
|
+
name: "JSX",
|
|
5417
|
+
syntax: "jsx",
|
|
5418
|
+
value: "jsx"
|
|
5419
|
+
},
|
|
5420
|
+
{
|
|
5421
|
+
name: "KaTeX",
|
|
5422
|
+
syntax: "simplemode",
|
|
5423
|
+
value: "katex"
|
|
5424
|
+
},
|
|
5425
|
+
{
|
|
5426
|
+
ext: ["kt"],
|
|
5427
|
+
name: "Kotlin",
|
|
5428
|
+
syntax: "text/x-kotlin",
|
|
5429
|
+
value: "kotlin"
|
|
5430
|
+
},
|
|
5431
|
+
{
|
|
5432
|
+
ext: ["less"],
|
|
5433
|
+
name: "Less",
|
|
5434
|
+
syntax: "css",
|
|
5435
|
+
value: "less"
|
|
5436
|
+
},
|
|
5437
|
+
{
|
|
5438
|
+
name: "Makefile",
|
|
5439
|
+
syntax: "cmake",
|
|
5440
|
+
value: "makefile"
|
|
5441
|
+
},
|
|
5442
|
+
{
|
|
5443
|
+
ext: [
|
|
5444
|
+
"markdown",
|
|
5445
|
+
"md",
|
|
5446
|
+
"mkd"
|
|
5447
|
+
],
|
|
5448
|
+
name: "Markdown",
|
|
5449
|
+
syntax: "markdown",
|
|
5450
|
+
value: "markdown"
|
|
5451
|
+
},
|
|
5452
|
+
{
|
|
5453
|
+
name: "MATLAB",
|
|
5454
|
+
syntax: "octave",
|
|
5455
|
+
value: "matlab"
|
|
5456
|
+
},
|
|
5457
|
+
{
|
|
5458
|
+
ext: ["conf"],
|
|
5459
|
+
name: "Nginx",
|
|
5460
|
+
syntax: "nginx",
|
|
5461
|
+
value: "nginx"
|
|
5462
|
+
},
|
|
5463
|
+
{
|
|
5464
|
+
ext: ["m"],
|
|
5465
|
+
name: "Objective-C",
|
|
5466
|
+
syntax: "text/x-objectivec",
|
|
5467
|
+
value: "objectivec"
|
|
5468
|
+
},
|
|
5469
|
+
{
|
|
5470
|
+
ext: ["p", "pas"],
|
|
5471
|
+
name: "Pascal",
|
|
5472
|
+
syntax: "pascal",
|
|
5473
|
+
value: "pascal"
|
|
5474
|
+
},
|
|
5475
|
+
{
|
|
5476
|
+
ext: ["pl", "pm"],
|
|
5477
|
+
name: "Perl",
|
|
5478
|
+
syntax: "perl",
|
|
5479
|
+
value: "perl"
|
|
5480
|
+
},
|
|
5481
|
+
{
|
|
5482
|
+
ext: [
|
|
5483
|
+
"php",
|
|
5484
|
+
"php3",
|
|
5485
|
+
"php4",
|
|
5486
|
+
"php5",
|
|
5487
|
+
"php7",
|
|
5488
|
+
"phtml"
|
|
5489
|
+
],
|
|
5490
|
+
name: "PHP",
|
|
5491
|
+
syntax: "text/x-php",
|
|
5492
|
+
value: "php"
|
|
5493
|
+
},
|
|
5494
|
+
{
|
|
5495
|
+
ext: [
|
|
5496
|
+
"ps1",
|
|
5497
|
+
"psd1",
|
|
5498
|
+
"psm1"
|
|
5499
|
+
],
|
|
5500
|
+
name: "PowerShell",
|
|
5501
|
+
syntax: "powershell",
|
|
5502
|
+
value: "powershell"
|
|
5503
|
+
},
|
|
5504
|
+
{
|
|
5505
|
+
ext: ["proto"],
|
|
5506
|
+
name: "Protobuf",
|
|
5507
|
+
syntax: "protobuf",
|
|
5508
|
+
value: "protobuf"
|
|
5509
|
+
},
|
|
5510
|
+
{
|
|
5511
|
+
ext: [
|
|
5512
|
+
"build",
|
|
5513
|
+
"bzl",
|
|
5514
|
+
"py",
|
|
5515
|
+
"pyw"
|
|
5516
|
+
],
|
|
5517
|
+
name: "Python",
|
|
5518
|
+
syntax: "python",
|
|
5519
|
+
value: "python"
|
|
5520
|
+
},
|
|
5521
|
+
{
|
|
5522
|
+
ext: ["r", "R"],
|
|
5523
|
+
name: "R",
|
|
5524
|
+
syntax: "r",
|
|
5525
|
+
value: "r"
|
|
5526
|
+
},
|
|
5527
|
+
{
|
|
5528
|
+
ext: ["rb"],
|
|
5529
|
+
name: "Ruby",
|
|
5530
|
+
syntax: "ruby",
|
|
5531
|
+
value: "ruby"
|
|
5532
|
+
},
|
|
5533
|
+
{
|
|
5534
|
+
ext: ["rs"],
|
|
5535
|
+
name: "Rust",
|
|
5536
|
+
syntax: "rust",
|
|
5537
|
+
value: "rust"
|
|
5538
|
+
},
|
|
5539
|
+
{
|
|
5540
|
+
ext: ["scala"],
|
|
5541
|
+
name: "Scala",
|
|
5542
|
+
syntax: "text/x-scala",
|
|
5543
|
+
value: "scala"
|
|
5544
|
+
},
|
|
5545
|
+
{
|
|
5546
|
+
ext: ["sh", "ksh"],
|
|
5547
|
+
name: "Shell",
|
|
5548
|
+
syntax: "shell",
|
|
5549
|
+
value: "shell"
|
|
5550
|
+
},
|
|
5551
|
+
{
|
|
5552
|
+
ext: ["sql"],
|
|
5553
|
+
name: "SQL",
|
|
5554
|
+
syntax: "text/x-sql",
|
|
5555
|
+
value: "sql"
|
|
5556
|
+
},
|
|
5557
|
+
{
|
|
5558
|
+
name: "PL/SQL",
|
|
5559
|
+
syntax: "text/x-plsql",
|
|
5560
|
+
value: "plsql"
|
|
5561
|
+
},
|
|
5562
|
+
{
|
|
5563
|
+
ext: ["swift"],
|
|
5564
|
+
name: "Swift",
|
|
5565
|
+
syntax: "swift",
|
|
5566
|
+
value: "swift"
|
|
5567
|
+
},
|
|
5568
|
+
{
|
|
5569
|
+
ext: ["ts"],
|
|
5570
|
+
name: "TypeScript",
|
|
5571
|
+
syntax: "text/typescript",
|
|
5572
|
+
value: "typescript"
|
|
5573
|
+
},
|
|
5574
|
+
{
|
|
5575
|
+
ext: ["vb"],
|
|
5576
|
+
name: "VB.net",
|
|
5577
|
+
syntax: "vb",
|
|
5578
|
+
value: "vbnet"
|
|
5579
|
+
},
|
|
5580
|
+
{
|
|
5581
|
+
ext: ["vtl"],
|
|
5582
|
+
name: "Velocity",
|
|
5583
|
+
syntax: "velocity",
|
|
5584
|
+
value: "velocity"
|
|
5585
|
+
},
|
|
5586
|
+
{
|
|
5587
|
+
ext: [
|
|
5588
|
+
"xml",
|
|
5589
|
+
"xsl",
|
|
5590
|
+
"xsd",
|
|
5591
|
+
"svg"
|
|
5592
|
+
],
|
|
5593
|
+
name: "XML",
|
|
5594
|
+
syntax: "xml",
|
|
5595
|
+
value: "xml"
|
|
5596
|
+
},
|
|
5597
|
+
{
|
|
5598
|
+
ext: ["yaml", "yml"],
|
|
5599
|
+
name: "YAML",
|
|
5600
|
+
syntax: "yaml",
|
|
5601
|
+
value: "yaml"
|
|
5602
|
+
},
|
|
5603
|
+
{
|
|
5604
|
+
name: "sTeX",
|
|
5605
|
+
syntax: "text/x-stex",
|
|
5606
|
+
value: "stex"
|
|
5607
|
+
},
|
|
5608
|
+
{
|
|
5609
|
+
ext: [
|
|
5610
|
+
"text",
|
|
5611
|
+
"ltx",
|
|
5612
|
+
"tex"
|
|
5613
|
+
],
|
|
5614
|
+
name: "LaTeX",
|
|
5615
|
+
syntax: "text/x-latex",
|
|
5616
|
+
value: "latex"
|
|
5617
|
+
},
|
|
5618
|
+
{
|
|
5619
|
+
ext: ["sv", "svh"],
|
|
5620
|
+
name: "SystemVerilog",
|
|
5621
|
+
syntax: "text/x-systemverilog",
|
|
5622
|
+
value: "systemverilog"
|
|
5623
|
+
},
|
|
5624
|
+
{
|
|
5625
|
+
ext: ["sass", "scss"],
|
|
5626
|
+
name: "Sass",
|
|
5627
|
+
syntax: "text/x-sass",
|
|
5628
|
+
value: "sass"
|
|
5629
|
+
},
|
|
5630
|
+
{
|
|
5631
|
+
ext: ["tcl"],
|
|
5632
|
+
name: "Tcl",
|
|
5633
|
+
syntax: "text/x-tcl",
|
|
5634
|
+
value: "tcl"
|
|
5635
|
+
},
|
|
5636
|
+
{
|
|
5637
|
+
ext: ["v"],
|
|
5638
|
+
name: "Verilog",
|
|
5639
|
+
syntax: "text/x-verilog",
|
|
5640
|
+
value: "verilog"
|
|
5641
|
+
},
|
|
5642
|
+
{
|
|
5643
|
+
name: "Vue",
|
|
5644
|
+
syntax: "text/x-vue",
|
|
5645
|
+
value: "vue"
|
|
5646
|
+
},
|
|
5647
|
+
{
|
|
5648
|
+
ext: ["lua"],
|
|
5649
|
+
name: "Lua",
|
|
5650
|
+
syntax: "text/x-lua",
|
|
5651
|
+
value: "lua"
|
|
5652
|
+
},
|
|
5653
|
+
{
|
|
5654
|
+
ext: ["hs"],
|
|
5655
|
+
name: "Haskell",
|
|
5656
|
+
syntax: "haskell",
|
|
5657
|
+
value: "haskell"
|
|
5658
|
+
},
|
|
5659
|
+
{
|
|
5660
|
+
ext: [
|
|
5661
|
+
"properties",
|
|
5662
|
+
"ini",
|
|
5663
|
+
"in"
|
|
5664
|
+
],
|
|
5665
|
+
name: "Properties",
|
|
5666
|
+
syntax: "properties",
|
|
5667
|
+
value: "properties"
|
|
5668
|
+
},
|
|
5669
|
+
{
|
|
5670
|
+
ext: ["toml"],
|
|
5671
|
+
name: "TOML",
|
|
5672
|
+
syntax: "toml",
|
|
5673
|
+
value: "toml"
|
|
5674
|
+
},
|
|
5675
|
+
{
|
|
5676
|
+
ext: ["cyp", "cypher"],
|
|
5677
|
+
name: "Cypher",
|
|
5678
|
+
syntax: "cypher",
|
|
5679
|
+
value: "cypher"
|
|
5680
|
+
},
|
|
5681
|
+
{
|
|
5682
|
+
ext: ["tsx"],
|
|
5683
|
+
name: "TSX",
|
|
5684
|
+
syntax: "jsx",
|
|
5685
|
+
value: "tsx"
|
|
5686
|
+
},
|
|
5687
|
+
{
|
|
5688
|
+
ext: ["fs"],
|
|
5689
|
+
name: "F#",
|
|
5690
|
+
syntax: "mllike",
|
|
5691
|
+
value: "f#"
|
|
5692
|
+
},
|
|
5693
|
+
{
|
|
5694
|
+
ext: [
|
|
5695
|
+
"ml",
|
|
5696
|
+
"mli",
|
|
5697
|
+
"mll",
|
|
5698
|
+
"mly"
|
|
5699
|
+
],
|
|
5700
|
+
name: "OCaml",
|
|
5701
|
+
syntax: "mllike",
|
|
5702
|
+
value: "ocaml"
|
|
5703
|
+
},
|
|
5704
|
+
{
|
|
5705
|
+
ext: [
|
|
5706
|
+
"clj",
|
|
5707
|
+
"cljc",
|
|
5708
|
+
"cljx"
|
|
5709
|
+
],
|
|
5710
|
+
name: "Clojure",
|
|
5711
|
+
syntax: "clojure",
|
|
5712
|
+
value: "clojure"
|
|
5713
|
+
},
|
|
5714
|
+
{
|
|
5715
|
+
name: "ABAP",
|
|
5716
|
+
syntax: "abap",
|
|
5717
|
+
value: "abap"
|
|
5718
|
+
},
|
|
5719
|
+
{
|
|
5720
|
+
ext: ["jl"],
|
|
5721
|
+
name: "Julia",
|
|
5722
|
+
syntax: "julia",
|
|
5723
|
+
value: "julia"
|
|
5724
|
+
},
|
|
5725
|
+
{
|
|
5726
|
+
ext: ["cmake"],
|
|
5727
|
+
name: "CMake",
|
|
5728
|
+
syntax: "cmake",
|
|
5729
|
+
value: "cmake"
|
|
5730
|
+
},
|
|
5731
|
+
{
|
|
5732
|
+
ext: ["scm", "ss"],
|
|
5733
|
+
name: "Scheme",
|
|
5734
|
+
syntax: "scheme",
|
|
5735
|
+
value: "scheme"
|
|
5736
|
+
},
|
|
5737
|
+
{
|
|
5738
|
+
ext: [
|
|
5739
|
+
"cl",
|
|
5740
|
+
"lisp",
|
|
5741
|
+
"el"
|
|
5742
|
+
],
|
|
5743
|
+
name: "Lisp",
|
|
5744
|
+
syntax: "commonlisp",
|
|
5745
|
+
value: "commonlisp"
|
|
5746
|
+
},
|
|
5747
|
+
{
|
|
5748
|
+
ext: [
|
|
5749
|
+
"f90",
|
|
5750
|
+
"f95",
|
|
5751
|
+
"f03"
|
|
5752
|
+
],
|
|
5753
|
+
name: "Fortran",
|
|
5754
|
+
syntax: "fortran",
|
|
5755
|
+
value: "fortran"
|
|
5756
|
+
},
|
|
5757
|
+
{
|
|
5758
|
+
ext: ["sol"],
|
|
5759
|
+
name: "Solidity",
|
|
5760
|
+
syntax: "solidity",
|
|
5761
|
+
value: "solidity"
|
|
5344
5762
|
}
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
...super.exportJSON(),
|
|
5370
|
-
rel: this.getRel(),
|
|
5371
|
-
target: this.getTarget(),
|
|
5372
|
-
title: this.getTitle(),
|
|
5373
|
-
url: this.getURL()
|
|
5374
|
-
};
|
|
5375
|
-
}
|
|
5376
|
-
getURL() {
|
|
5377
|
-
return this.getLatest().__url;
|
|
5378
|
-
}
|
|
5379
|
-
setURL(url) {
|
|
5380
|
-
const writable = this.getWritable();
|
|
5381
|
-
writable.__url = url;
|
|
5382
|
-
return writable;
|
|
5383
|
-
}
|
|
5384
|
-
getTarget() {
|
|
5385
|
-
return this.getLatest().__target;
|
|
5763
|
+
];
|
|
5764
|
+
LANGUAGES.sort((modeA, modeB) => {
|
|
5765
|
+
const nameA = modeA.name.toLowerCase();
|
|
5766
|
+
const nameB = modeB.name.toLowerCase();
|
|
5767
|
+
if (nameA === nameB) return 0;
|
|
5768
|
+
if (nameA < nameB) return -1;
|
|
5769
|
+
return 1;
|
|
5770
|
+
});
|
|
5771
|
+
LANGUAGES.unshift({
|
|
5772
|
+
name: "Plain Text",
|
|
5773
|
+
syntax: "simplemode",
|
|
5774
|
+
value: "plain"
|
|
5775
|
+
});
|
|
5776
|
+
//#endregion
|
|
5777
|
+
//#region src/plugins/codemirror-block/lib/mode.ts
|
|
5778
|
+
function modeMatch(mode = "") {
|
|
5779
|
+
mode = mode.toLocaleLowerCase() || "plain";
|
|
5780
|
+
return LANGUAGES.find((m) => m.value === mode || m.ext?.includes(mode))?.value || "plain";
|
|
5781
|
+
}
|
|
5782
|
+
//#endregion
|
|
5783
|
+
//#region src/plugins/codemirror-block/plugin/index.ts
|
|
5784
|
+
const CodemirrorPlugin = class extends KernelPlugin {
|
|
5785
|
+
static {
|
|
5786
|
+
this.pluginName = "CodemirrorPlugin";
|
|
5386
5787
|
}
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5788
|
+
constructor(kernel, config) {
|
|
5789
|
+
super();
|
|
5790
|
+
this.kernel = kernel;
|
|
5791
|
+
kernel.registerNodes([CodeMirrorNode]);
|
|
5792
|
+
kernel.registerThemes({ hr: config?.theme || "" });
|
|
5793
|
+
this.registerDecorator(kernel, "codemirror", (node, editor) => {
|
|
5794
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
5795
|
+
});
|
|
5391
5796
|
}
|
|
5392
|
-
|
|
5393
|
-
|
|
5797
|
+
onInit(editor) {
|
|
5798
|
+
this.register(registerCodeMirrorCommand(editor));
|
|
5799
|
+
this.registerMarkdown();
|
|
5800
|
+
this.registerLiteXml();
|
|
5394
5801
|
}
|
|
5395
|
-
|
|
5396
|
-
const
|
|
5397
|
-
|
|
5398
|
-
|
|
5802
|
+
registerLiteXml() {
|
|
5803
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
5804
|
+
if (!litexmlService) return;
|
|
5805
|
+
litexmlService.registerXMLWriter(CodeMirrorNode.getType(), (node, ctx) => {
|
|
5806
|
+
const codeMirrorNode = node;
|
|
5807
|
+
return ctx.createXmlNode("code", { lang: codeMirrorNode.lang || "plain" }, codeMirrorNode.code);
|
|
5808
|
+
});
|
|
5809
|
+
litexmlService.registerXMLReader("code", (xmlElement, children) => {
|
|
5810
|
+
const text = children.map((v) => v.text || "").join("");
|
|
5811
|
+
const language = xmlElement.getAttribute("lang") || "plain";
|
|
5812
|
+
return INodeHelper.createTypeNode("code", {
|
|
5813
|
+
code: text || xmlElement.textContent || "",
|
|
5814
|
+
language: modeMatch(language),
|
|
5815
|
+
version: 1
|
|
5816
|
+
});
|
|
5817
|
+
});
|
|
5399
5818
|
}
|
|
5400
|
-
|
|
5401
|
-
|
|
5819
|
+
registerMarkdown() {
|
|
5820
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
5821
|
+
markdownService?.registerMarkdownShortCut({
|
|
5822
|
+
regExp: /^(```|···)(.+)?$/,
|
|
5823
|
+
replace: (parentNode, _, match) => {
|
|
5824
|
+
const code = $createCodeMirrorNode(modeMatch(match[2]), "");
|
|
5825
|
+
parentNode.replace(code);
|
|
5826
|
+
const sel = $createNodeSelection();
|
|
5827
|
+
sel.add(code.getKey());
|
|
5828
|
+
$setSelection(sel);
|
|
5829
|
+
},
|
|
5830
|
+
trigger: "enter",
|
|
5831
|
+
type: "element"
|
|
5832
|
+
});
|
|
5833
|
+
markdownService?.registerMarkdownWriter(CodeMirrorNode.getType(), (ctx, node) => {
|
|
5834
|
+
if (node instanceof CodeMirrorNode) {
|
|
5835
|
+
ctx.appendLine("```" + node.lang);
|
|
5836
|
+
ctx.appendLine("\n");
|
|
5837
|
+
ctx.appendLine(node.code);
|
|
5838
|
+
ctx.appendLine("\n```\n");
|
|
5839
|
+
}
|
|
5840
|
+
});
|
|
5841
|
+
markdownService?.registerMarkdownReader("code", (node) => {
|
|
5842
|
+
const language = node.lang ? modeMatch(node.lang) : "plain";
|
|
5843
|
+
return INodeHelper.createTypeNode("code", {
|
|
5844
|
+
code: node.value,
|
|
5845
|
+
language,
|
|
5846
|
+
version: 1
|
|
5847
|
+
});
|
|
5848
|
+
});
|
|
5402
5849
|
}
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5850
|
+
};
|
|
5851
|
+
//#endregion
|
|
5852
|
+
//#region src/plugins/upload/service/i-upload-service.ts
|
|
5853
|
+
const IUploadService = genServiceId("UploadService");
|
|
5854
|
+
//#endregion
|
|
5855
|
+
//#region src/plugins/file/node/FileNode.ts
|
|
5856
|
+
var FileNode = class FileNode extends DecoratorNode {
|
|
5857
|
+
static getType() {
|
|
5858
|
+
return "file";
|
|
5407
5859
|
}
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
rel: this.__rel,
|
|
5411
|
-
target: this.__target,
|
|
5412
|
-
title: this.__title
|
|
5413
|
-
});
|
|
5414
|
-
this.insertAfter(linkNode, restoreSelection);
|
|
5415
|
-
return linkNode;
|
|
5860
|
+
static clone(node) {
|
|
5861
|
+
return new FileNode(node.__name, node.__fileUrl, node.__size, node.__status, node.__message, node.__key);
|
|
5416
5862
|
}
|
|
5417
|
-
|
|
5418
|
-
return
|
|
5863
|
+
static importJSON(serializedNode) {
|
|
5864
|
+
return new FileNode(serializedNode.name, serializedNode.fileUrl, serializedNode.size, serializedNode.status, serializedNode.message);
|
|
5419
5865
|
}
|
|
5420
|
-
|
|
5421
|
-
return
|
|
5866
|
+
static importDOM() {
|
|
5867
|
+
return { span: (node) => {
|
|
5868
|
+
if (node.classList.contains("file")) return {
|
|
5869
|
+
conversion: $convertFileElement,
|
|
5870
|
+
priority: 0
|
|
5871
|
+
};
|
|
5872
|
+
return null;
|
|
5873
|
+
} };
|
|
5422
5874
|
}
|
|
5423
|
-
|
|
5424
|
-
return
|
|
5875
|
+
get name() {
|
|
5876
|
+
return this.__name;
|
|
5425
5877
|
}
|
|
5426
|
-
|
|
5427
|
-
return
|
|
5878
|
+
get fileUrl() {
|
|
5879
|
+
return this.__fileUrl;
|
|
5428
5880
|
}
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
const anchorNode = selection.anchor.getNode();
|
|
5432
|
-
const focusNode = selection.focus.getNode();
|
|
5433
|
-
return this.isParentOf(anchorNode) && this.isParentOf(focusNode) && selection.getTextContent().length > 0;
|
|
5881
|
+
get size() {
|
|
5882
|
+
return this.__size;
|
|
5434
5883
|
}
|
|
5435
|
-
|
|
5436
|
-
return this.
|
|
5884
|
+
get status() {
|
|
5885
|
+
return this.__status;
|
|
5437
5886
|
}
|
|
5438
|
-
|
|
5439
|
-
return this.
|
|
5887
|
+
get message() {
|
|
5888
|
+
return this.__message;
|
|
5440
5889
|
}
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
target: domNode.getAttribute("target"),
|
|
5449
|
-
title: domNode.getAttribute("title")
|
|
5450
|
-
});
|
|
5451
|
-
}
|
|
5452
|
-
return { node };
|
|
5453
|
-
}
|
|
5454
|
-
/**
|
|
5455
|
-
* Takes a URL and creates a LinkNode.
|
|
5456
|
-
* @param url - The URL the LinkNode should direct to.
|
|
5457
|
-
* @param attributes - Optional HTML a tag attributes \\{ target, rel, title \\}
|
|
5458
|
-
* @returns The LinkNode.
|
|
5459
|
-
*/
|
|
5460
|
-
function $createLinkNode(url = "", attributes) {
|
|
5461
|
-
return $applyNodeReplacement(new LinkNode(url, attributes));
|
|
5462
|
-
}
|
|
5463
|
-
/**
|
|
5464
|
-
* Determines if node is a LinkNode.
|
|
5465
|
-
* @param node - The node to be checked.
|
|
5466
|
-
* @returns true if node is a LinkNode, false otherwise.
|
|
5467
|
-
*/
|
|
5468
|
-
function $isLinkNode(node) {
|
|
5469
|
-
return node instanceof LinkNode;
|
|
5470
|
-
}
|
|
5471
|
-
var AutoLinkNode = class AutoLinkNode extends LinkNode {
|
|
5472
|
-
constructor(url = "", attributes = {}, key) {
|
|
5473
|
-
super(url, attributes, key);
|
|
5474
|
-
this.__isUnlinked = attributes.isUnlinked !== void 0 && attributes.isUnlinked !== null ? attributes.isUnlinked : false;
|
|
5475
|
-
}
|
|
5476
|
-
static getType() {
|
|
5477
|
-
return "autolink";
|
|
5478
|
-
}
|
|
5479
|
-
static clone(node) {
|
|
5480
|
-
return new AutoLinkNode(node.__url, {
|
|
5481
|
-
isUnlinked: node.__isUnlinked,
|
|
5482
|
-
rel: node.__rel,
|
|
5483
|
-
target: node.__target,
|
|
5484
|
-
title: node.__title
|
|
5485
|
-
}, node.__key);
|
|
5890
|
+
constructor(name, fileUrl, size, status, message, key) {
|
|
5891
|
+
super(key);
|
|
5892
|
+
this.__name = name;
|
|
5893
|
+
this.__fileUrl = fileUrl;
|
|
5894
|
+
this.__size = size;
|
|
5895
|
+
this.__status = status || "pending";
|
|
5896
|
+
this.__message = message;
|
|
5486
5897
|
}
|
|
5487
|
-
|
|
5488
|
-
|
|
5898
|
+
setUploaded(url) {
|
|
5899
|
+
const writable = this.getWritable();
|
|
5900
|
+
writable.__fileUrl = url;
|
|
5901
|
+
writable.__status = "uploaded";
|
|
5902
|
+
writable.__message = void 0;
|
|
5489
5903
|
}
|
|
5490
|
-
|
|
5491
|
-
const
|
|
5492
|
-
|
|
5493
|
-
|
|
5904
|
+
setError(message) {
|
|
5905
|
+
const writable = this.getWritable();
|
|
5906
|
+
writable.__status = "error";
|
|
5907
|
+
writable.__message = message;
|
|
5494
5908
|
}
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
if (this.__isUnlinked) return document.createElement("span");
|
|
5498
|
-
else return super.createDOM(config, editor);
|
|
5909
|
+
exportDOM() {
|
|
5910
|
+
return { element: document.createElement("span") };
|
|
5499
5911
|
}
|
|
5500
|
-
|
|
5501
|
-
|
|
5912
|
+
createDOM(config) {
|
|
5913
|
+
const element = document.createElement("span");
|
|
5914
|
+
addClassNamesToElement(element, config.theme.file);
|
|
5915
|
+
return element;
|
|
5502
5916
|
}
|
|
5503
|
-
|
|
5504
|
-
return
|
|
5917
|
+
exportJSON() {
|
|
5918
|
+
return {
|
|
5919
|
+
...super.exportJSON(),
|
|
5920
|
+
fileUrl: this.fileUrl,
|
|
5921
|
+
message: this.message,
|
|
5922
|
+
name: this.name,
|
|
5923
|
+
size: this.size,
|
|
5924
|
+
status: this.status
|
|
5925
|
+
};
|
|
5505
5926
|
}
|
|
5506
5927
|
updateFromJSON(serializedNode) {
|
|
5507
|
-
return super.updateFromJSON(serializedNode)
|
|
5928
|
+
return super.updateFromJSON(serializedNode);
|
|
5508
5929
|
}
|
|
5509
|
-
|
|
5510
|
-
return
|
|
5930
|
+
getTextContent() {
|
|
5931
|
+
return "\n";
|
|
5511
5932
|
}
|
|
5512
|
-
|
|
5933
|
+
updateDOM() {
|
|
5934
|
+
return false;
|
|
5935
|
+
}
|
|
5936
|
+
decorate(editor) {
|
|
5937
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator("file");
|
|
5938
|
+
if (!decorator) return null;
|
|
5939
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
5513
5940
|
return {
|
|
5514
|
-
|
|
5515
|
-
|
|
5941
|
+
queryDOM: decorator.queryDOM,
|
|
5942
|
+
render: decorator.render(this, editor)
|
|
5516
5943
|
};
|
|
5517
5944
|
}
|
|
5518
|
-
insertNewAfter(selection, restoreSelection = true) {
|
|
5519
|
-
const element = this.getParentOrThrow().insertNewAfter(selection, restoreSelection);
|
|
5520
|
-
if ($isElementNode(element)) {
|
|
5521
|
-
const linkNode = $createAutoLinkNode(this.__url, {
|
|
5522
|
-
isUnlinked: this.__isUnlinked,
|
|
5523
|
-
rel: this.__rel,
|
|
5524
|
-
target: this.__target,
|
|
5525
|
-
title: this.__title
|
|
5526
|
-
});
|
|
5527
|
-
element.append(linkNode);
|
|
5528
|
-
return linkNode;
|
|
5529
|
-
}
|
|
5530
|
-
return null;
|
|
5531
|
-
}
|
|
5532
5945
|
};
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
* during typing, which is especially useful when a button to generate a LinkNode is not practical.
|
|
5536
|
-
* @param url - The URL the LinkNode should direct to.
|
|
5537
|
-
* @param attributes - Optional HTML a tag attributes. \\{ target, rel, title \\}
|
|
5538
|
-
* @returns The LinkNode.
|
|
5539
|
-
*/
|
|
5540
|
-
function $createAutoLinkNode(url = "", attributes) {
|
|
5541
|
-
return $applyNodeReplacement(new AutoLinkNode(url, attributes));
|
|
5946
|
+
function $createFileNode(name = "unknown", fileUrl, size, status, message) {
|
|
5947
|
+
return $applyNodeReplacement(new FileNode(name, fileUrl, size, status, message));
|
|
5542
5948
|
}
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
* @param node - The node to be checked.
|
|
5546
|
-
* @returns true if node is an AutoLinkNode, false otherwise.
|
|
5547
|
-
*/
|
|
5548
|
-
function $isAutoLinkNode(node) {
|
|
5549
|
-
return node instanceof AutoLinkNode;
|
|
5949
|
+
function $convertFileElement() {
|
|
5950
|
+
return { node: $createFileNode() };
|
|
5550
5951
|
}
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
if (point.type === "element") {
|
|
5554
|
-
const node = point.getNode();
|
|
5555
|
-
assert($isElementNode(node), "$getPointNode: element point is not an ElementNode");
|
|
5556
|
-
return node.getChildren()[point.offset + offset] || null;
|
|
5557
|
-
}
|
|
5558
|
-
return null;
|
|
5952
|
+
function $isFileNode(node) {
|
|
5953
|
+
return node.getType() === FileNode.getType();
|
|
5559
5954
|
}
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
const focusParent = focusNode.getParent();
|
|
5585
|
-
if (focusParent) finalSelection.focus.set(focusParent.getKey(), focusNode.getIndexWithinParent() + (isBackwards ? 0 : 1), "element");
|
|
5586
|
-
}
|
|
5587
|
-
$setSelection($normalizeSelection__EXPERIMENTAL(finalSelection));
|
|
5588
|
-
}
|
|
5589
|
-
}
|
|
5590
|
-
return rval;
|
|
5955
|
+
//#endregion
|
|
5956
|
+
//#region src/plugins/file/command/index.ts
|
|
5957
|
+
const logger$2 = createDebugLogger("plugin", "file");
|
|
5958
|
+
const INSERT_FILE_COMMAND = createCommand("INSERT_FILE_COMMAND");
|
|
5959
|
+
function registerFileCommand(editor, handleUpload) {
|
|
5960
|
+
return editor.registerCommand(INSERT_FILE_COMMAND, (payload) => {
|
|
5961
|
+
const { file } = payload;
|
|
5962
|
+
editor.update(() => {
|
|
5963
|
+
const fileNode = $createFileNode(file.name);
|
|
5964
|
+
$insertNodes([fileNode]);
|
|
5965
|
+
if ($isRootOrShadowRoot(fileNode.getParentOrThrow())) $wrapNodeInElement(fileNode, $createParagraphNode).selectEnd();
|
|
5966
|
+
handleUpload(file).then((url) => {
|
|
5967
|
+
editor.update(() => {
|
|
5968
|
+
fileNode.setUploaded(url.url);
|
|
5969
|
+
});
|
|
5970
|
+
}).catch((error) => {
|
|
5971
|
+
logger$2.error("❌ File upload failed:", error);
|
|
5972
|
+
editor.update(() => {
|
|
5973
|
+
fileNode.setError("File upload failed : " + error.message);
|
|
5974
|
+
});
|
|
5975
|
+
});
|
|
5976
|
+
});
|
|
5977
|
+
return false;
|
|
5978
|
+
}, COMMAND_PRIORITY_HIGH);
|
|
5591
5979
|
}
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5980
|
+
//#endregion
|
|
5981
|
+
//#region src/plugins/file/utils/index.ts
|
|
5982
|
+
function registerFileNodeSelectionObserver(editor) {
|
|
5983
|
+
const selectFileKeys = [];
|
|
5984
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
5985
|
+
const selection = editorState.read(() => $getSelection());
|
|
5986
|
+
const newSelectFileKeys = [];
|
|
5987
|
+
if ($isNodeSelection(selection)) editorState.read(() => selection.getNodes()).forEach((node) => {
|
|
5988
|
+
if (node.getType() === "file") newSelectFileKeys.push(node.getKey());
|
|
5989
|
+
});
|
|
5990
|
+
else if ($isRangeSelection(selection) && !selection.isCollapsed()) editorState.read(() => {
|
|
5991
|
+
selection.getNodes().forEach((node) => {
|
|
5992
|
+
if (node.getType() === "file") newSelectFileKeys.push(node.getKey());
|
|
5993
|
+
});
|
|
5994
|
+
});
|
|
5995
|
+
const removeKeys = selectFileKeys.filter((key) => !newSelectFileKeys.includes(key));
|
|
5996
|
+
const addKeys = newSelectFileKeys.filter((key) => !selectFileKeys.includes(key));
|
|
5997
|
+
selectFileKeys.length = 0;
|
|
5998
|
+
selectFileKeys.push(...newSelectFileKeys);
|
|
5999
|
+
removeKeys.forEach((key) => {
|
|
6000
|
+
editor.getElementByKey(key)?.classList.remove("selected");
|
|
6001
|
+
});
|
|
6002
|
+
addKeys.forEach((key) => {
|
|
6003
|
+
editor.getElementByKey(key)?.classList.add("selected");
|
|
6004
|
+
});
|
|
6005
|
+
});
|
|
6006
|
+
}
|
|
6007
|
+
//#endregion
|
|
6008
|
+
//#region src/plugins/file/plugin/index.ts
|
|
6009
|
+
const FilePlugin = class extends KernelPlugin {
|
|
6010
|
+
static {
|
|
6011
|
+
this.pluginName = "FilePlugin";
|
|
6012
|
+
}
|
|
6013
|
+
constructor(kernel, config) {
|
|
6014
|
+
super();
|
|
6015
|
+
this.kernel = kernel;
|
|
6016
|
+
this.config = config;
|
|
6017
|
+
this.logger = createDebugLogger("plugin", "file");
|
|
6018
|
+
kernel.registerNodes([FileNode]);
|
|
6019
|
+
if (config?.theme) kernel.registerThemes(config?.theme);
|
|
6020
|
+
this.registerDecorator(kernel, FileNode.getType(), (node, editor) => {
|
|
6021
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
6022
|
+
});
|
|
6023
|
+
}
|
|
6024
|
+
onInit(editor) {
|
|
6025
|
+
const handleUpload = this.config?.handleUpload;
|
|
6026
|
+
if (handleUpload) {
|
|
6027
|
+
this.kernel.requireService(IUploadService)?.registerUpload(async (file, from, range) => {
|
|
6028
|
+
editor.update(() => {
|
|
6029
|
+
if (range) {
|
|
6030
|
+
const rangeSelection = $createRangeSelection();
|
|
6031
|
+
if (range !== null && range !== void 0) rangeSelection.applyDOMRange(range);
|
|
6032
|
+
$setSelection(rangeSelection);
|
|
6033
|
+
}
|
|
6034
|
+
const fileNode = $createFileNode(file.name);
|
|
6035
|
+
$insertNodes([fileNode]);
|
|
6036
|
+
if ($isRootOrShadowRoot(fileNode.getParentOrThrow())) $wrapNodeInElement(fileNode, $createParagraphNode).selectEnd();
|
|
6037
|
+
handleUpload(file).then((url) => {
|
|
6038
|
+
editor.update(() => {
|
|
6039
|
+
fileNode.setUploaded(url.url);
|
|
6040
|
+
});
|
|
6041
|
+
}).catch((error) => {
|
|
6042
|
+
this.logger.error("File upload failed:", error);
|
|
6043
|
+
editor.update(() => {
|
|
6044
|
+
fileNode.setError("File upload failed : " + error.message);
|
|
6045
|
+
});
|
|
5623
6046
|
});
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
6047
|
+
});
|
|
6048
|
+
return null;
|
|
6049
|
+
});
|
|
6050
|
+
this.register(registerFileCommand(editor, handleUpload));
|
|
6051
|
+
}
|
|
6052
|
+
if (this.config?.decorator) this.register(registerFileNodeSelectionObserver(editor));
|
|
6053
|
+
this.registerLiteXml();
|
|
6054
|
+
this.registerMarkdownWriter();
|
|
6055
|
+
}
|
|
6056
|
+
registerLiteXml() {
|
|
6057
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
6058
|
+
if (!litexmlService) return;
|
|
6059
|
+
litexmlService.registerXMLWriter(FileNode.getType(), (node, ctx) => {
|
|
6060
|
+
if ($isFileNode(node)) return ctx.createXmlNode("file", {
|
|
6061
|
+
fileUrl: node.fileUrl || "",
|
|
6062
|
+
message: node.message || "",
|
|
6063
|
+
name: node.name,
|
|
6064
|
+
size: node.size?.toString() || "0",
|
|
6065
|
+
status: node.status
|
|
6066
|
+
});
|
|
6067
|
+
return false;
|
|
6068
|
+
});
|
|
6069
|
+
litexmlService.registerXMLReader("file", (xmlElement) => {
|
|
6070
|
+
const name = xmlElement.getAttribute("name") || "unknown";
|
|
6071
|
+
const fileUrl = xmlElement.getAttribute("fileUrl") || "";
|
|
6072
|
+
const status = xmlElement.getAttribute("status");
|
|
6073
|
+
return INodeHelper.createTypeNode(FileNode.getType(), {
|
|
6074
|
+
fileUrl,
|
|
6075
|
+
message: xmlElement.getAttribute("message") || "",
|
|
6076
|
+
name,
|
|
6077
|
+
size: parseInt(xmlElement.getAttribute("size") || "0", 10),
|
|
6078
|
+
status
|
|
6079
|
+
});
|
|
5628
6080
|
});
|
|
5629
|
-
return;
|
|
5630
6081
|
}
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
if (
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
6082
|
+
registerMarkdownWriter() {
|
|
6083
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
6084
|
+
if (!markdownService) return;
|
|
6085
|
+
markdownService.registerMarkdownWriter(FileNode.getType(), (ctx, node) => {
|
|
6086
|
+
if ($isFileNode(node)) {
|
|
6087
|
+
if (this.config?.markdownWriter) {
|
|
6088
|
+
ctx.appendLine(this.config.markdownWriter(node));
|
|
6089
|
+
return;
|
|
6090
|
+
}
|
|
6091
|
+
if (node.status === "pending") ctx.appendLine(`Uploading ${node.name}...`);
|
|
6092
|
+
else if (node.status === "error") ctx.appendLine(`Failed to upload ${node.name}: ${node.message}`);
|
|
6093
|
+
else ctx.appendLine(`[${node.name}](${node.fileUrl})`);
|
|
5639
6094
|
}
|
|
5640
6095
|
});
|
|
5641
|
-
return;
|
|
5642
6096
|
}
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
if (rel !== void 0) linkNode.setRel(rel);
|
|
5650
|
-
if (title !== void 0) linkNode.setTitle(title);
|
|
5651
|
-
};
|
|
5652
|
-
if (nodes.length === 1) {
|
|
5653
|
-
const firstNode = nodes[0];
|
|
5654
|
-
const linkNode = $getAncestor(firstNode, $isLinkNode);
|
|
5655
|
-
if (linkNode !== null) return updateLinkNode(linkNode);
|
|
6097
|
+
};
|
|
6098
|
+
//#endregion
|
|
6099
|
+
//#region src/plugins/hr/node/HorizontalRuleNode.ts
|
|
6100
|
+
var HorizontalRuleNode = class HorizontalRuleNode extends DecoratorNode {
|
|
6101
|
+
static getType() {
|
|
6102
|
+
return "horizontalrule";
|
|
5656
6103
|
}
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
6104
|
+
static clone(node) {
|
|
6105
|
+
return new HorizontalRuleNode(node.__key);
|
|
6106
|
+
}
|
|
6107
|
+
static importJSON(serializedNode) {
|
|
6108
|
+
return $createHorizontalRuleNode().updateFromJSON(serializedNode);
|
|
6109
|
+
}
|
|
6110
|
+
static importDOM() {
|
|
6111
|
+
return { hr: () => ({
|
|
6112
|
+
conversion: $convertHorizontalRuleElement,
|
|
6113
|
+
priority: 0
|
|
6114
|
+
}) };
|
|
6115
|
+
}
|
|
6116
|
+
exportDOM() {
|
|
6117
|
+
return { element: document.createElement("hr") };
|
|
6118
|
+
}
|
|
6119
|
+
createDOM(config) {
|
|
6120
|
+
const element = document.createElement("div");
|
|
6121
|
+
addClassNamesToElement(element, config.theme.hr);
|
|
6122
|
+
return element;
|
|
6123
|
+
}
|
|
6124
|
+
getTextContent() {
|
|
6125
|
+
return "\n";
|
|
6126
|
+
}
|
|
6127
|
+
isInline() {
|
|
6128
|
+
return false;
|
|
6129
|
+
}
|
|
6130
|
+
updateDOM() {
|
|
6131
|
+
return false;
|
|
6132
|
+
}
|
|
6133
|
+
decorate(editor) {
|
|
6134
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator("horizontalrule");
|
|
6135
|
+
if (!decorator) return null;
|
|
6136
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
6137
|
+
return {
|
|
6138
|
+
queryDOM: decorator.queryDOM,
|
|
6139
|
+
render: decorator.render(this, editor)
|
|
6140
|
+
};
|
|
6141
|
+
}
|
|
6142
|
+
};
|
|
6143
|
+
function $createHorizontalRuleNode() {
|
|
6144
|
+
return $applyNodeReplacement(new HorizontalRuleNode());
|
|
5693
6145
|
}
|
|
5694
|
-
function $
|
|
5695
|
-
|
|
5696
|
-
while (parent !== null && parent.getParent() !== null && !predicate(parent)) parent = parent.getParentOrThrow();
|
|
5697
|
-
return predicate(parent) ? parent : null;
|
|
6146
|
+
function $convertHorizontalRuleElement() {
|
|
6147
|
+
return { node: $createHorizontalRuleNode() };
|
|
5698
6148
|
}
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
* Formats a URL string by adding appropriate protocol if missing
|
|
5702
|
-
*
|
|
5703
|
-
* @param url - URL to format
|
|
5704
|
-
* @returns Formatted URL with appropriate protocol
|
|
5705
|
-
*/
|
|
5706
|
-
function formatUrl(url) {
|
|
5707
|
-
if (/^[a-z][\d+.a-z-]*:/i.test(url)) return url;
|
|
5708
|
-
else if (/^[#./]/.test(url)) return url;
|
|
5709
|
-
else if (url.includes("@")) return `mailto:${url}`;
|
|
5710
|
-
else if (PHONE_NUMBER_REGEX.test(url)) return `tel:${url}`;
|
|
5711
|
-
return url;
|
|
6149
|
+
function $isHorizontalRuleNode(node) {
|
|
6150
|
+
return node.getType() === HorizontalRuleNode.getType();
|
|
5712
6151
|
}
|
|
5713
6152
|
//#endregion
|
|
5714
|
-
//#region src/plugins/
|
|
5715
|
-
const
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
return mergeRegister(editor.registerCommand(INSERT_LINK_COMMAND, (payload) => {
|
|
5719
|
-
const { url, title = url } = payload;
|
|
5720
|
-
editor.update(() => {
|
|
5721
|
-
const linkNode = $createLinkNode(url, { title });
|
|
5722
|
-
const textNode = $createTextNode(title);
|
|
5723
|
-
linkNode.append(textNode);
|
|
5724
|
-
$insertNodes([linkNode]);
|
|
5725
|
-
});
|
|
5726
|
-
return false;
|
|
5727
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(UPDATE_LINK_TEXT_COMMAND, (payload) => {
|
|
5728
|
-
const { key, text } = payload;
|
|
6153
|
+
//#region src/plugins/hr/command/index.ts
|
|
6154
|
+
const INSERT_HORIZONTAL_RULE_COMMAND = createCommand("INSERT_HORIZONTAL_RULE_COMMAND");
|
|
6155
|
+
function registerHorizontalRuleCommand(editor) {
|
|
6156
|
+
return editor.registerCommand(INSERT_HORIZONTAL_RULE_COMMAND, () => {
|
|
5729
6157
|
editor.update(() => {
|
|
5730
|
-
|
|
5731
|
-
if (linkNode) {
|
|
5732
|
-
const newLinkNode = $createLinkNode(linkNode.getURL(), { title: text });
|
|
5733
|
-
const textNode = $createTextNode(text);
|
|
5734
|
-
newLinkNode.append(textNode);
|
|
5735
|
-
linkNode?.replace(newLinkNode);
|
|
5736
|
-
newLinkNode.select(1);
|
|
5737
|
-
}
|
|
6158
|
+
$insertNodes([$createHorizontalRuleNode()]);
|
|
5738
6159
|
});
|
|
5739
|
-
return
|
|
5740
|
-
}, COMMAND_PRIORITY_EDITOR)
|
|
6160
|
+
return true;
|
|
6161
|
+
}, COMMAND_PRIORITY_EDITOR);
|
|
5741
6162
|
}
|
|
5742
6163
|
//#endregion
|
|
5743
|
-
//#region src/plugins/
|
|
5744
|
-
const
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
super(..._args);
|
|
5748
|
-
this._enableLinkToolbar = true;
|
|
6164
|
+
//#region src/plugins/hr/plugin/index.ts
|
|
6165
|
+
const HRPlugin = class extends KernelPlugin {
|
|
6166
|
+
static {
|
|
6167
|
+
this.pluginName = "HRPlugin";
|
|
5749
6168
|
}
|
|
5750
|
-
|
|
5751
|
-
|
|
6169
|
+
constructor(kernel, config) {
|
|
6170
|
+
super();
|
|
6171
|
+
this.kernel = kernel;
|
|
6172
|
+
kernel.registerNodes([HorizontalRuleNode]);
|
|
6173
|
+
kernel.registerThemes({ hr: config?.theme || "" });
|
|
6174
|
+
this.registerDecorator(kernel, "horizontalrule", (node, editor) => {
|
|
6175
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
6176
|
+
});
|
|
5752
6177
|
}
|
|
5753
|
-
|
|
5754
|
-
this.
|
|
5755
|
-
this.
|
|
6178
|
+
onInit(editor) {
|
|
6179
|
+
this.register(registerHorizontalRuleCommand(editor));
|
|
6180
|
+
this.registerMarkdown();
|
|
6181
|
+
this.registerLiteXml();
|
|
6182
|
+
}
|
|
6183
|
+
registerLiteXml() {
|
|
6184
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
6185
|
+
if (!litexmlService) return;
|
|
6186
|
+
litexmlService.registerXMLWriter(HorizontalRuleNode.getType(), (node, ctx) => {
|
|
6187
|
+
if ($isHorizontalRuleNode(node)) return ctx.createXmlNode("hr", {});
|
|
6188
|
+
return false;
|
|
6189
|
+
});
|
|
6190
|
+
litexmlService.registerXMLReader("hr", () => {
|
|
6191
|
+
return INodeHelper.createElementNode(HorizontalRuleNode.getType(), {});
|
|
6192
|
+
});
|
|
6193
|
+
}
|
|
6194
|
+
registerMarkdown() {
|
|
6195
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownShortCut({
|
|
6196
|
+
regExp: /^(---|\*\*\*|___)$/,
|
|
6197
|
+
replace: (parentNode, _1, _2, isImport) => {
|
|
6198
|
+
const line = $createHorizontalRuleNode();
|
|
6199
|
+
if (isImport || parentNode.getNextSibling()) parentNode.replace(line);
|
|
6200
|
+
else parentNode.insertBefore(line);
|
|
6201
|
+
line.selectNext();
|
|
6202
|
+
},
|
|
6203
|
+
trigger: "enter",
|
|
6204
|
+
type: "element"
|
|
6205
|
+
});
|
|
6206
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(HorizontalRuleNode.getType(), (ctx, node) => {
|
|
6207
|
+
if ($isHorizontalRuleNode(node)) ctx.appendLine("---\n\n");
|
|
6208
|
+
});
|
|
6209
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("thematicBreak", () => {
|
|
6210
|
+
return INodeHelper.createElementNode("horizontalrule", {});
|
|
6211
|
+
});
|
|
5756
6212
|
}
|
|
5757
6213
|
};
|
|
5758
6214
|
//#endregion
|
|
5759
|
-
//#region src/plugins/
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
"mailto:",
|
|
5764
|
-
"sms:",
|
|
5765
|
-
"tel:"
|
|
5766
|
-
]);
|
|
5767
|
-
function sanitizeUrl(url) {
|
|
5768
|
-
try {
|
|
5769
|
-
const parsedUrl = new URL(url);
|
|
5770
|
-
if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) return "about:blank";
|
|
5771
|
-
} catch {
|
|
5772
|
-
return url;
|
|
6215
|
+
//#region src/plugins/image/node/basie-image-node.ts
|
|
6216
|
+
var BaseImageNode = class BaseImageNode extends DecoratorNode {
|
|
6217
|
+
static clone(node) {
|
|
6218
|
+
return new BaseImageNode(node.__src, node.__altText, node.__maxWidth, node.__width, node.__height, node.__key);
|
|
5773
6219
|
}
|
|
5774
|
-
|
|
5775
|
-
}
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
6220
|
+
static importJSON(serializedNode) {
|
|
6221
|
+
const { altText, height, width, maxWidth, src } = serializedNode;
|
|
6222
|
+
return new BaseImageNode(src, altText, maxWidth, width, height);
|
|
6223
|
+
}
|
|
6224
|
+
static getType() {
|
|
6225
|
+
return "image";
|
|
6226
|
+
}
|
|
6227
|
+
updateFromJSON(serializedNode) {
|
|
6228
|
+
return super.updateFromJSON(serializedNode);
|
|
6229
|
+
}
|
|
6230
|
+
exportDOM() {
|
|
6231
|
+
const element = document.createElement("img");
|
|
6232
|
+
element.setAttribute("src", this.__src);
|
|
6233
|
+
element.setAttribute("alt", this.__altText);
|
|
6234
|
+
element.setAttribute("width", this.__width.toString());
|
|
6235
|
+
element.setAttribute("height", this.__height.toString());
|
|
6236
|
+
return { element };
|
|
6237
|
+
}
|
|
6238
|
+
constructor(src, altText, maxWidth, width, height, key) {
|
|
6239
|
+
super(key);
|
|
6240
|
+
this.__src = src;
|
|
6241
|
+
this.__altText = altText;
|
|
6242
|
+
this.__maxWidth = maxWidth;
|
|
6243
|
+
this.__width = width || "inherit";
|
|
6244
|
+
this.__height = height || "inherit";
|
|
6245
|
+
}
|
|
6246
|
+
exportJSON() {
|
|
6247
|
+
return {
|
|
6248
|
+
...super.exportJSON(),
|
|
6249
|
+
altText: this.getAltText(),
|
|
6250
|
+
height: this.__height === "inherit" ? 0 : this.__height,
|
|
6251
|
+
maxWidth: this.__maxWidth,
|
|
6252
|
+
src: this.getSrc(),
|
|
6253
|
+
width: this.__width === "inherit" ? 0 : this.__width
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
6256
|
+
setWidthAndHeight(width, height) {
|
|
6257
|
+
const writable = this.getWritable();
|
|
6258
|
+
writable.__width = width;
|
|
6259
|
+
writable.__height = height;
|
|
6260
|
+
}
|
|
6261
|
+
isInline() {
|
|
6262
|
+
return true;
|
|
6263
|
+
}
|
|
6264
|
+
createDOM(config) {
|
|
6265
|
+
const span = document.createElement("span");
|
|
6266
|
+
const className = config.theme.image;
|
|
6267
|
+
if (className !== void 0) span.className = className;
|
|
6268
|
+
return span;
|
|
6269
|
+
}
|
|
6270
|
+
updateDOM() {
|
|
6271
|
+
return false;
|
|
6272
|
+
}
|
|
6273
|
+
getSrc() {
|
|
6274
|
+
return this.__src;
|
|
6275
|
+
}
|
|
6276
|
+
getAltText() {
|
|
6277
|
+
return this.__altText;
|
|
6278
|
+
}
|
|
6279
|
+
};
|
|
6280
|
+
//#endregion
|
|
6281
|
+
//#region src/plugins/image/node/block-image-node.tsx
|
|
6282
|
+
var BlockImageNode = class BlockImageNode extends BaseImageNode {
|
|
6283
|
+
static {
|
|
6284
|
+
this._decorate = () => null;
|
|
6285
|
+
}
|
|
6286
|
+
static setDecorate(decorate) {
|
|
6287
|
+
BlockImageNode._decorate = decorate;
|
|
6288
|
+
}
|
|
6289
|
+
static getType() {
|
|
6290
|
+
return "block-image";
|
|
6291
|
+
}
|
|
6292
|
+
get isLoading() {
|
|
6293
|
+
return this.__loading;
|
|
6294
|
+
}
|
|
6295
|
+
get status() {
|
|
6296
|
+
return this.__status;
|
|
6297
|
+
}
|
|
6298
|
+
get message() {
|
|
6299
|
+
return this.__message;
|
|
6300
|
+
}
|
|
6301
|
+
get src() {
|
|
6302
|
+
return this.__src;
|
|
6303
|
+
}
|
|
6304
|
+
get altText() {
|
|
6305
|
+
return this.__altText;
|
|
6306
|
+
}
|
|
6307
|
+
get maxWidth() {
|
|
6308
|
+
return this.__maxWidth;
|
|
6309
|
+
}
|
|
6310
|
+
get width() {
|
|
6311
|
+
return this.__width;
|
|
6312
|
+
}
|
|
6313
|
+
get height() {
|
|
6314
|
+
return this.__height;
|
|
6315
|
+
}
|
|
6316
|
+
constructor(opt) {
|
|
6317
|
+
super(opt.src, opt.altText, opt.maxWidth, opt.width, opt.height, opt.key);
|
|
6318
|
+
this.__loading = true;
|
|
6319
|
+
this.__status = "uploaded";
|
|
6320
|
+
this.__message = null;
|
|
6321
|
+
this.__extra = null;
|
|
6322
|
+
this.__status = opt.status ?? "uploaded";
|
|
6323
|
+
}
|
|
6324
|
+
isInline() {
|
|
6325
|
+
return false;
|
|
6326
|
+
}
|
|
6327
|
+
setMaxWidth(maxWidth) {
|
|
6328
|
+
const writable = this.getWritable();
|
|
6329
|
+
writable.__maxWidth = maxWidth;
|
|
6330
|
+
}
|
|
6331
|
+
setWidth(width) {
|
|
6332
|
+
const writable = this.getWritable();
|
|
6333
|
+
writable.__width = width;
|
|
6334
|
+
}
|
|
6335
|
+
setStatus(status) {
|
|
6336
|
+
const writable = this.getWritable();
|
|
6337
|
+
writable.__status = status;
|
|
6338
|
+
}
|
|
6339
|
+
setUploaded(url) {
|
|
6340
|
+
const writable = this.getWritable();
|
|
6341
|
+
writable.__loading = false;
|
|
6342
|
+
writable.__src = url;
|
|
6343
|
+
writable.__status = "uploaded";
|
|
6344
|
+
}
|
|
6345
|
+
setError(message) {
|
|
6346
|
+
const writable = this.getWritable();
|
|
6347
|
+
writable.__loading = false;
|
|
6348
|
+
writable.__status = "error";
|
|
6349
|
+
writable.__message = message;
|
|
6350
|
+
}
|
|
6351
|
+
static clone(node) {
|
|
6352
|
+
return new BlockImageNode({
|
|
6353
|
+
altText: node.__altText,
|
|
6354
|
+
height: node.__height,
|
|
6355
|
+
key: node.__key,
|
|
6356
|
+
maxWidth: node.__maxWidth,
|
|
6357
|
+
src: node.__src,
|
|
6358
|
+
status: node.__status,
|
|
6359
|
+
width: node.__width
|
|
6360
|
+
});
|
|
6361
|
+
}
|
|
6362
|
+
static importJSON(serializedNode) {
|
|
6363
|
+
const { altText, height, width, maxWidth, src, status } = serializedNode;
|
|
6364
|
+
return $createBlockImageNode({
|
|
6365
|
+
altText,
|
|
6366
|
+
height,
|
|
6367
|
+
maxWidth,
|
|
6368
|
+
src,
|
|
6369
|
+
status,
|
|
6370
|
+
width
|
|
6371
|
+
}).updateFromJSON(serializedNode);
|
|
6372
|
+
}
|
|
6373
|
+
static importDOM() {
|
|
6374
|
+
return { img: () => ({
|
|
6375
|
+
conversion: $convertImageElement$1,
|
|
6376
|
+
priority: 0
|
|
6377
|
+
}) };
|
|
6378
|
+
}
|
|
6379
|
+
decorate() {
|
|
6380
|
+
return BlockImageNode._decorate(this);
|
|
6381
|
+
}
|
|
6382
|
+
exportJSON() {
|
|
6383
|
+
return {
|
|
6384
|
+
...super.exportJSON(),
|
|
6385
|
+
status: this.__status
|
|
6386
|
+
};
|
|
6387
|
+
}
|
|
6388
|
+
createDOM(config) {
|
|
6389
|
+
const span = document.createElement("div");
|
|
6390
|
+
const className = config.theme.blockImage;
|
|
6391
|
+
if (className !== void 0) span.className = className;
|
|
6392
|
+
return span;
|
|
6393
|
+
}
|
|
6394
|
+
};
|
|
6395
|
+
function $createBlockImageNode({ altText, height, maxWidth = 4200, src, width, key, status }) {
|
|
6396
|
+
return $applyNodeReplacement(new BlockImageNode({
|
|
6397
|
+
altText,
|
|
6398
|
+
height,
|
|
6399
|
+
key,
|
|
6400
|
+
maxWidth,
|
|
6401
|
+
src,
|
|
6402
|
+
status,
|
|
6403
|
+
width
|
|
6404
|
+
}));
|
|
6405
|
+
}
|
|
6406
|
+
function $convertImageElement$1(domNode) {
|
|
6407
|
+
const img = domNode;
|
|
6408
|
+
if (img.src.startsWith("file:///")) return null;
|
|
6409
|
+
const { alt: altText, src, width, height } = img;
|
|
6410
|
+
return { node: $createBlockImageNode({
|
|
6411
|
+
altText,
|
|
6412
|
+
height,
|
|
6413
|
+
src,
|
|
6414
|
+
width
|
|
6415
|
+
}) };
|
|
6416
|
+
}
|
|
6417
|
+
function $isBlockImageNode(node) {
|
|
6418
|
+
return node.getType() === BlockImageNode.getType();
|
|
6419
|
+
}
|
|
6420
|
+
//#endregion
|
|
6421
|
+
//#region src/plugins/image/node/image-node.tsx
|
|
6422
|
+
var ImageNode = class ImageNode extends BaseImageNode {
|
|
6423
|
+
static {
|
|
6424
|
+
this._decorate = () => null;
|
|
6425
|
+
}
|
|
6426
|
+
static setDecorate(decorate) {
|
|
6427
|
+
ImageNode._decorate = decorate;
|
|
6428
|
+
}
|
|
6429
|
+
static getType() {
|
|
6430
|
+
return "image";
|
|
6431
|
+
}
|
|
6432
|
+
constructor(opt) {
|
|
6433
|
+
super(opt.src, opt.altText, opt.maxWidth, opt.width, opt.height, opt.key);
|
|
6434
|
+
this.__loading = true;
|
|
6435
|
+
this.__status = "uploaded";
|
|
6436
|
+
this.__message = null;
|
|
6437
|
+
this.__extra = null;
|
|
6438
|
+
this.__status = opt.status ?? "uploaded";
|
|
6439
|
+
}
|
|
6440
|
+
get isLoading() {
|
|
6441
|
+
return this.__loading;
|
|
6442
|
+
}
|
|
6443
|
+
get status() {
|
|
6444
|
+
return this.__status;
|
|
6445
|
+
}
|
|
6446
|
+
get message() {
|
|
6447
|
+
return this.__message;
|
|
6448
|
+
}
|
|
6449
|
+
get src() {
|
|
6450
|
+
return this.__src;
|
|
6451
|
+
}
|
|
6452
|
+
get altText() {
|
|
6453
|
+
return this.__altText;
|
|
6454
|
+
}
|
|
6455
|
+
get maxWidth() {
|
|
6456
|
+
return this.__maxWidth;
|
|
6457
|
+
}
|
|
6458
|
+
get width() {
|
|
6459
|
+
return this.__width;
|
|
6460
|
+
}
|
|
6461
|
+
get height() {
|
|
6462
|
+
return this.__height;
|
|
6463
|
+
}
|
|
6464
|
+
setMaxWidth(maxWidth) {
|
|
6465
|
+
const writable = this.getWritable();
|
|
6466
|
+
writable.__maxWidth = maxWidth;
|
|
6467
|
+
}
|
|
6468
|
+
setStatus(status) {
|
|
6469
|
+
const writable = this.getWritable();
|
|
6470
|
+
writable.__status = status;
|
|
6471
|
+
}
|
|
6472
|
+
setWidth(width) {
|
|
6473
|
+
const writable = this.getWritable();
|
|
6474
|
+
writable.__width = width;
|
|
6475
|
+
}
|
|
6476
|
+
setUploaded(url) {
|
|
6477
|
+
const writable = this.getWritable();
|
|
6478
|
+
writable.__loading = false;
|
|
6479
|
+
writable.__src = url;
|
|
6480
|
+
writable.__status = "uploaded";
|
|
6481
|
+
}
|
|
6482
|
+
setError(message) {
|
|
6483
|
+
const writable = this.getWritable();
|
|
6484
|
+
writable.__loading = false;
|
|
6485
|
+
writable.__status = "error";
|
|
6486
|
+
writable.__message = message;
|
|
6487
|
+
}
|
|
6488
|
+
static clone(node) {
|
|
6489
|
+
return new ImageNode({
|
|
6490
|
+
altText: node.__altText,
|
|
6491
|
+
height: node.__height,
|
|
6492
|
+
key: node.__key,
|
|
6493
|
+
maxWidth: node.__maxWidth,
|
|
6494
|
+
src: node.__src,
|
|
6495
|
+
status: node.__status,
|
|
6496
|
+
width: node.__width
|
|
5854
6497
|
});
|
|
6498
|
+
}
|
|
6499
|
+
static importJSON(serializedNode) {
|
|
6500
|
+
const { altText, height, width, maxWidth, src, status } = serializedNode;
|
|
6501
|
+
return $createImageNode({
|
|
6502
|
+
altText,
|
|
6503
|
+
height,
|
|
6504
|
+
maxWidth,
|
|
6505
|
+
src,
|
|
6506
|
+
status,
|
|
6507
|
+
width
|
|
6508
|
+
}).updateFromJSON(serializedNode);
|
|
6509
|
+
}
|
|
6510
|
+
static importDOM() {
|
|
6511
|
+
return { img: () => ({
|
|
6512
|
+
conversion: $convertImageElement,
|
|
6513
|
+
priority: 0
|
|
6514
|
+
}) };
|
|
6515
|
+
}
|
|
6516
|
+
decorate() {
|
|
6517
|
+
return ImageNode._decorate(this);
|
|
6518
|
+
}
|
|
6519
|
+
exportJSON() {
|
|
6520
|
+
return {
|
|
6521
|
+
...super.exportJSON(),
|
|
6522
|
+
status: this.__status
|
|
6523
|
+
};
|
|
6524
|
+
}
|
|
6525
|
+
};
|
|
6526
|
+
function $createImageNode({ altText, height, maxWidth = 500, src, width, key, status }) {
|
|
6527
|
+
return $applyNodeReplacement(new ImageNode({
|
|
6528
|
+
altText,
|
|
6529
|
+
height,
|
|
6530
|
+
key,
|
|
6531
|
+
maxWidth,
|
|
6532
|
+
src,
|
|
6533
|
+
status,
|
|
6534
|
+
width
|
|
6535
|
+
}));
|
|
6536
|
+
}
|
|
6537
|
+
function $convertImageElement(domNode) {
|
|
6538
|
+
const img = domNode;
|
|
6539
|
+
if (img.src.startsWith("file:///")) return null;
|
|
6540
|
+
const { alt: altText, src, width, height } = img;
|
|
6541
|
+
return { node: $createImageNode({
|
|
6542
|
+
altText,
|
|
6543
|
+
height,
|
|
6544
|
+
src,
|
|
6545
|
+
width
|
|
6546
|
+
}) };
|
|
6547
|
+
}
|
|
6548
|
+
function $isImageNode(node) {
|
|
6549
|
+
return node.getType() === ImageNode.getType();
|
|
6550
|
+
}
|
|
6551
|
+
//#endregion
|
|
6552
|
+
//#region src/plugins/image/command/index.ts
|
|
6553
|
+
const logger$1 = createDebugLogger("plugin", "image");
|
|
6554
|
+
const INSERT_IMAGE_COMMAND = createCommand("INSERT_IMAGE_COMMAND");
|
|
6555
|
+
function isImageFile(file) {
|
|
6556
|
+
return file.type.startsWith("image/");
|
|
6557
|
+
}
|
|
6558
|
+
function registerImageCommand(editor, handleUpload, defaultBlockImage = false) {
|
|
6559
|
+
return editor.registerCommand(INSERT_IMAGE_COMMAND, (payload) => {
|
|
6560
|
+
const { file, range, block, maxWidth } = payload;
|
|
6561
|
+
const isBlock = block ?? defaultBlockImage;
|
|
6562
|
+
if (!isImageFile(file)) return false;
|
|
6563
|
+
const placeholderURL = URL.createObjectURL(file);
|
|
5855
6564
|
editor.update(() => {
|
|
5856
|
-
if (
|
|
5857
|
-
const
|
|
5858
|
-
if (
|
|
5859
|
-
|
|
5860
|
-
selection.anchor.set(anchorNode.getKey(), expandTo.index, "text");
|
|
5861
|
-
selection.focus.set(anchorNode.getKey(), expandTo.index + expandTo.length, "text");
|
|
5862
|
-
}
|
|
6565
|
+
if (range) {
|
|
6566
|
+
const rangeSelection = $createRangeSelection();
|
|
6567
|
+
if (range !== null && range !== void 0) rangeSelection.applyDOMRange(range);
|
|
6568
|
+
$setSelection(rangeSelection);
|
|
5863
6569
|
}
|
|
5864
|
-
|
|
6570
|
+
const imageNode = isBlock ? $createBlockImageNode({
|
|
6571
|
+
altText: file.name,
|
|
6572
|
+
maxWidth: maxWidth || 800,
|
|
6573
|
+
src: placeholderURL,
|
|
6574
|
+
status: "loading"
|
|
6575
|
+
}) : $createImageNode({
|
|
6576
|
+
altText: file.name,
|
|
6577
|
+
maxWidth: maxWidth || 800,
|
|
6578
|
+
src: placeholderURL,
|
|
6579
|
+
status: "loading"
|
|
6580
|
+
});
|
|
6581
|
+
$insertNodes([imageNode]);
|
|
6582
|
+
if (!isBlock && $isRootOrShadowRoot(imageNode.getParentOrThrow())) $wrapNodeInElement(imageNode, $createParagraphNode).selectEnd();
|
|
6583
|
+
handleUpload(file).then((res) => {
|
|
6584
|
+
editor.update(() => {
|
|
6585
|
+
imageNode.setUploaded(res.url);
|
|
6586
|
+
});
|
|
6587
|
+
}).catch((error) => {
|
|
6588
|
+
logger$1.error("❌ Image upload failed:", error);
|
|
6589
|
+
editor.update(() => {
|
|
6590
|
+
imageNode.setError("Image upload failed : " + error.message);
|
|
6591
|
+
});
|
|
6592
|
+
});
|
|
5865
6593
|
});
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
preventDefault: true,
|
|
5869
|
-
stopPropagation: true
|
|
5870
|
-
}));
|
|
5871
|
-
return mergeRegister(...registrations);
|
|
6594
|
+
return true;
|
|
6595
|
+
}, COMMAND_PRIORITY_EDITOR);
|
|
5872
6596
|
}
|
|
5873
6597
|
//#endregion
|
|
5874
|
-
//#region src/plugins/
|
|
5875
|
-
const
|
|
6598
|
+
//#region src/plugins/image/plugin/index.ts
|
|
6599
|
+
const ImagePlugin = class extends KernelPlugin {
|
|
5876
6600
|
static {
|
|
5877
|
-
this.pluginName = "
|
|
6601
|
+
this.pluginName = "ImagePlugin";
|
|
5878
6602
|
}
|
|
5879
6603
|
constructor(kernel, config) {
|
|
5880
6604
|
super();
|
|
5881
6605
|
this.kernel = kernel;
|
|
5882
6606
|
this.config = config;
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
kernel.registerService(ILinkService, this.service);
|
|
6607
|
+
kernel.registerNodes([ImageNode, BlockImageNode]);
|
|
6608
|
+
ImageNode.setDecorate(config?.renderImage ?? (() => null));
|
|
6609
|
+
BlockImageNode.setDecorate(config?.renderImage ?? (() => null));
|
|
5887
6610
|
if (config?.theme) kernel.registerThemes(config.theme);
|
|
5888
|
-
if (config?.linkRegex) this.linkRegex = config.linkRegex;
|
|
5889
6611
|
}
|
|
5890
6612
|
onInit(editor) {
|
|
5891
|
-
this.register(
|
|
5892
|
-
this.register(registerLinkCommands(editor, this.kernel, {
|
|
5893
|
-
attributes: this.config?.attributes,
|
|
5894
|
-
enableHotkey: this.config?.enableHotkey,
|
|
5895
|
-
validateUrl: this.config?.validateUrl
|
|
5896
|
-
}));
|
|
5897
|
-
this.register(editor.registerCommand(PASTE_COMMAND, (payload) => {
|
|
5898
|
-
const { clipboardData } = payload;
|
|
5899
|
-
if (clipboardData && clipboardData.types && clipboardData.types.length === 1 && clipboardData.types[0] === "text/plain") {
|
|
5900
|
-
const data = clipboardData.getData("text/plain").trim();
|
|
5901
|
-
if (this.linkRegex.test(data)) {
|
|
5902
|
-
payload.stopImmediatePropagation();
|
|
5903
|
-
payload.preventDefault();
|
|
5904
|
-
editor.dispatchCommand(INSERT_LINK_COMMAND, { url: data });
|
|
5905
|
-
return true;
|
|
5906
|
-
}
|
|
5907
|
-
}
|
|
5908
|
-
return false;
|
|
5909
|
-
}, COMMAND_PRIORITY_NORMAL));
|
|
6613
|
+
if (this.config?.handleUpload) this.register(registerImageCommand(editor, this.config.handleUpload, this.config?.defaultBlockImage !== false));
|
|
5910
6614
|
this.registerMarkdown();
|
|
5911
6615
|
this.registerLiteXml();
|
|
6616
|
+
this.registerINode();
|
|
6617
|
+
this.registerUpload(editor);
|
|
6618
|
+
if (this.config?.needRehost && this.config?.handleRehost) {
|
|
6619
|
+
const needRehost = this.config.needRehost;
|
|
6620
|
+
const handleRehost = this.config.handleRehost;
|
|
6621
|
+
this.register(editor.registerNodeTransform(ImageNode, (node) => {
|
|
6622
|
+
if (node.status === "uploaded" && needRehost(node.src)) {
|
|
6623
|
+
node.setStatus("loading");
|
|
6624
|
+
handleRehost(node.src).then(({ url }) => {
|
|
6625
|
+
editor.update(() => {
|
|
6626
|
+
node.setUploaded(url);
|
|
6627
|
+
});
|
|
6628
|
+
}).catch(() => {
|
|
6629
|
+
editor.update(() => {
|
|
6630
|
+
node.setError("Rehost failed");
|
|
6631
|
+
});
|
|
6632
|
+
});
|
|
6633
|
+
}
|
|
6634
|
+
}));
|
|
6635
|
+
this.register(editor.registerNodeTransform(BlockImageNode, (node) => {
|
|
6636
|
+
if (node.status === "uploaded" && needRehost(node.src)) {
|
|
6637
|
+
node.setStatus("loading");
|
|
6638
|
+
handleRehost(node.src).then(({ url }) => {
|
|
6639
|
+
editor.update(() => {
|
|
6640
|
+
node.setUploaded(url);
|
|
6641
|
+
});
|
|
6642
|
+
}).catch(() => {
|
|
6643
|
+
editor.update(() => {
|
|
6644
|
+
node.setError("Rehost failed");
|
|
6645
|
+
});
|
|
6646
|
+
});
|
|
6647
|
+
}
|
|
6648
|
+
}));
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6651
|
+
registerUpload(editor) {
|
|
6652
|
+
const uploadService = this.kernel.requireService(IUploadService);
|
|
6653
|
+
if (!uploadService) return;
|
|
6654
|
+
if (!this.config?.handleUpload) return;
|
|
6655
|
+
uploadService.registerUpload(async (file, from, range) => {
|
|
6656
|
+
const imageWidth = await this.config?.getImageWidth?.(file);
|
|
6657
|
+
return editor.dispatchCommand(INSERT_IMAGE_COMMAND, {
|
|
6658
|
+
block: this.config?.defaultBlockImage !== false,
|
|
6659
|
+
file,
|
|
6660
|
+
maxWidth: imageWidth,
|
|
6661
|
+
range
|
|
6662
|
+
});
|
|
6663
|
+
}, 0);
|
|
5912
6664
|
}
|
|
5913
6665
|
registerLiteXml() {
|
|
5914
6666
|
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
5915
6667
|
if (!litexmlService) return;
|
|
5916
|
-
litexmlService.registerXMLWriter(
|
|
5917
|
-
if ($
|
|
5918
|
-
const attributes = {
|
|
5919
|
-
|
|
6668
|
+
litexmlService.registerXMLWriter(ImageNode.getType(), (node, ctx) => {
|
|
6669
|
+
if ($isImageNode(node)) {
|
|
6670
|
+
const attributes = { src: node.src };
|
|
6671
|
+
if (node.altText) attributes.alt = node.altText;
|
|
6672
|
+
return ctx.createXmlNode("img", attributes);
|
|
5920
6673
|
}
|
|
5921
6674
|
return false;
|
|
5922
6675
|
});
|
|
5923
|
-
litexmlService.
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
6676
|
+
litexmlService.registerXMLWriter(BlockImageNode.getType(), (node, ctx) => {
|
|
6677
|
+
if ($isBlockImageNode(node)) {
|
|
6678
|
+
const attributes = {
|
|
6679
|
+
block: "true",
|
|
6680
|
+
src: node.src
|
|
6681
|
+
};
|
|
6682
|
+
if (node.altText) attributes.alt = node.altText;
|
|
6683
|
+
if (node.width) attributes.width = String(node.width);
|
|
6684
|
+
if (node.maxWidth) attributes["max-width"] = String(node.maxWidth);
|
|
6685
|
+
return ctx.createXmlNode("img", attributes);
|
|
6686
|
+
}
|
|
6687
|
+
return false;
|
|
6688
|
+
});
|
|
6689
|
+
litexmlService.registerXMLReader("img", (xmlNode) => {
|
|
6690
|
+
if (this.config?.defaultBlockImage !== false) return INodeHelper.createElementNode(BlockImageNode.getType(), {
|
|
6691
|
+
altText: xmlNode.getAttribute("alt") || "",
|
|
6692
|
+
maxWidth: xmlNode.getAttribute("max-width") ? parseInt(xmlNode.getAttribute("max-width"), 10) : void 0,
|
|
6693
|
+
src: xmlNode.getAttribute("src") || "",
|
|
6694
|
+
width: xmlNode.getAttribute("width") ? parseInt(xmlNode.getAttribute("width"), 10) : void 0
|
|
6695
|
+
});
|
|
6696
|
+
if (xmlNode.getAttribute("block") === "true") return INodeHelper.createElementNode(BlockImageNode.getType(), {
|
|
6697
|
+
altText: xmlNode.getAttribute("alt") || "",
|
|
6698
|
+
maxWidth: xmlNode.getAttribute("max-width") ? parseInt(xmlNode.getAttribute("max-width"), 10) : void 0,
|
|
6699
|
+
src: xmlNode.getAttribute("src") || "",
|
|
6700
|
+
width: xmlNode.getAttribute("width") ? parseInt(xmlNode.getAttribute("width"), 10) : void 0
|
|
6701
|
+
});
|
|
6702
|
+
else return INodeHelper.createElementNode(ImageNode.getType(), {
|
|
6703
|
+
altText: xmlNode.getAttribute("alt") || "",
|
|
6704
|
+
maxWidth: xmlNode.getAttribute("max-width") ? parseInt(xmlNode.getAttribute("max-width"), 10) : void 0,
|
|
6705
|
+
src: xmlNode.getAttribute("src") || "",
|
|
6706
|
+
width: xmlNode.getAttribute("width") ? parseInt(xmlNode.getAttribute("width"), 10) : void 0
|
|
6707
|
+
});
|
|
5933
6708
|
});
|
|
5934
6709
|
}
|
|
5935
6710
|
registerMarkdown() {
|
|
5936
|
-
this.
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
6711
|
+
const defaultBlockImage = this.config?.defaultBlockImage !== false;
|
|
6712
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
6713
|
+
if (!markdownService) return;
|
|
6714
|
+
markdownService.registerMarkdownWriter(ImageNode.getType(), (ctx, node) => {
|
|
6715
|
+
if ($isImageNode(node)) ctx.appendLine(``);
|
|
6716
|
+
});
|
|
6717
|
+
markdownService.registerMarkdownWriter(BlockImageNode.getType(), (ctx, node) => {
|
|
6718
|
+
if ($isBlockImageNode(node)) ctx.appendLine(`\n\n`);
|
|
6719
|
+
});
|
|
6720
|
+
markdownService.registerMarkdownReader("image", (node) => {
|
|
6721
|
+
const altText = node.alt;
|
|
6722
|
+
const src = node.url;
|
|
6723
|
+
return INodeHelper.createTypeNode(defaultBlockImage ? BlockImageNode.getType() : ImageNode.getType(), {
|
|
6724
|
+
altText,
|
|
6725
|
+
showCaption: false,
|
|
6726
|
+
src,
|
|
6727
|
+
version: 1
|
|
6728
|
+
});
|
|
6729
|
+
});
|
|
6730
|
+
}
|
|
6731
|
+
registerINode() {
|
|
6732
|
+
const service = this.kernel.requireService(INodeService);
|
|
6733
|
+
if (!service) return;
|
|
6734
|
+
service.registerProcessNodeTree(({ root }) => {
|
|
6735
|
+
const loopNodes = (node) => {
|
|
6736
|
+
if ("children" in node && Array.isArray(node.children)) {
|
|
6737
|
+
if (node.type === "paragraph" && node.children.length === 1 && node.children[0].type === BlockImageNode.getType()) return node.children[0];
|
|
6738
|
+
node.children = node.children.map((child) => {
|
|
6739
|
+
return loopNodes(child);
|
|
6740
|
+
});
|
|
6741
|
+
}
|
|
6742
|
+
return node;
|
|
6743
|
+
};
|
|
6744
|
+
root.children = root.children.map((child) => {
|
|
6745
|
+
return loopNodes(child);
|
|
6746
|
+
});
|
|
6747
|
+
});
|
|
6748
|
+
}
|
|
6749
|
+
};
|
|
6750
|
+
//#endregion
|
|
6751
|
+
//#region src/plugins/link/node/LinkNode.ts
|
|
6752
|
+
const logger = createDebugLogger("plugin", "link");
|
|
6753
|
+
const SUPPORTED_URL_PROTOCOLS$1 = new Set([
|
|
6754
|
+
"http:",
|
|
6755
|
+
"https:",
|
|
6756
|
+
"mailto:",
|
|
6757
|
+
"sms:",
|
|
6758
|
+
"tel:"
|
|
6759
|
+
]);
|
|
6760
|
+
const HOVER_LINK_COMMAND = createCommand("HOVER_LINK_COMMAND");
|
|
6761
|
+
const HOVER_OUT_LINK_COMMAND = createCommand("HOVER_OUT_LINK_COMMAND");
|
|
6762
|
+
/** @noInheritDoc */
|
|
6763
|
+
var LinkNode = class LinkNode extends ElementNode {
|
|
6764
|
+
static getType() {
|
|
6765
|
+
return "link";
|
|
6766
|
+
}
|
|
6767
|
+
static clone(node) {
|
|
6768
|
+
return new LinkNode(node.__url, {
|
|
6769
|
+
rel: node.__rel,
|
|
6770
|
+
target: node.__target,
|
|
6771
|
+
title: node.__title
|
|
6772
|
+
}, node.__key);
|
|
6773
|
+
}
|
|
6774
|
+
constructor(url = "", attributes = {}, key) {
|
|
6775
|
+
super(key);
|
|
6776
|
+
const { target = null, rel = null, title = null } = attributes;
|
|
6777
|
+
this.__url = url;
|
|
6778
|
+
this.__target = target;
|
|
6779
|
+
this.__rel = rel;
|
|
6780
|
+
this.__title = title;
|
|
6781
|
+
}
|
|
6782
|
+
createDOM(config, editor) {
|
|
6783
|
+
logger.debug("🔍 config", config);
|
|
6784
|
+
const element = document.createElement("a");
|
|
6785
|
+
this.updateLinkDOM(null, element, config);
|
|
6786
|
+
addClassNamesToElement(element, config.theme.link);
|
|
6787
|
+
element.addEventListener("mouseenter", (event) => {
|
|
6788
|
+
if (event.target instanceof HTMLElement) {
|
|
6789
|
+
event.target.classList.add("hover");
|
|
6790
|
+
editor.dispatchCommand(HOVER_LINK_COMMAND, {
|
|
6791
|
+
event,
|
|
6792
|
+
linkNode: this
|
|
6793
|
+
});
|
|
6794
|
+
}
|
|
6795
|
+
});
|
|
6796
|
+
element.addEventListener("mouseleave", (event) => {
|
|
6797
|
+
if (event.target instanceof HTMLElement) {
|
|
6798
|
+
event.target.classList.remove("hover");
|
|
6799
|
+
editor.dispatchCommand(HOVER_OUT_LINK_COMMAND, { event });
|
|
6800
|
+
}
|
|
6801
|
+
});
|
|
6802
|
+
return element;
|
|
6803
|
+
}
|
|
6804
|
+
updateLinkDOM(prevNode, anchor, _config) {
|
|
6805
|
+
if (isHTMLAnchorElement(anchor)) {
|
|
6806
|
+
if (!prevNode || prevNode.__url !== this.__url) anchor.href = this.sanitizeUrl(this.__url);
|
|
6807
|
+
for (const attr of [
|
|
6808
|
+
"target",
|
|
6809
|
+
"rel",
|
|
6810
|
+
"title"
|
|
6811
|
+
]) {
|
|
6812
|
+
const key = `__${attr}`;
|
|
6813
|
+
const value = this[key];
|
|
6814
|
+
if (!prevNode || prevNode[key] !== value) if (value) anchor[attr] = value;
|
|
6815
|
+
else anchor.removeAttribute(attr);
|
|
6816
|
+
}
|
|
6817
|
+
}
|
|
6818
|
+
}
|
|
6819
|
+
updateDOM(prevNode, anchor, config) {
|
|
6820
|
+
this.updateLinkDOM(prevNode, anchor, config);
|
|
6821
|
+
return false;
|
|
6822
|
+
}
|
|
6823
|
+
static importDOM() {
|
|
6824
|
+
return { a: () => ({
|
|
6825
|
+
conversion: $convertAnchorElement,
|
|
6826
|
+
priority: 1
|
|
6827
|
+
}) };
|
|
6828
|
+
}
|
|
6829
|
+
static importJSON(serializedNode) {
|
|
6830
|
+
return $createLinkNode().updateFromJSON(serializedNode);
|
|
6831
|
+
}
|
|
6832
|
+
updateFromJSON(serializedNode) {
|
|
6833
|
+
return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setRel(serializedNode.rel || null).setTarget(serializedNode.target || null).setTitle(serializedNode.title || null);
|
|
6834
|
+
}
|
|
6835
|
+
sanitizeUrl(url) {
|
|
6836
|
+
url = formatUrl(url);
|
|
6837
|
+
try {
|
|
6838
|
+
const parsedUrl = new URL(formatUrl(url));
|
|
6839
|
+
if (!SUPPORTED_URL_PROTOCOLS$1.has(parsedUrl.protocol)) return "about:blank";
|
|
6840
|
+
} catch {
|
|
6841
|
+
return url;
|
|
6842
|
+
}
|
|
6843
|
+
return url;
|
|
6844
|
+
}
|
|
6845
|
+
exportJSON() {
|
|
6846
|
+
return {
|
|
6847
|
+
...super.exportJSON(),
|
|
6848
|
+
rel: this.getRel(),
|
|
6849
|
+
target: this.getTarget(),
|
|
6850
|
+
title: this.getTitle(),
|
|
6851
|
+
url: this.getURL()
|
|
6852
|
+
};
|
|
6853
|
+
}
|
|
6854
|
+
getURL() {
|
|
6855
|
+
return this.getLatest().__url;
|
|
6856
|
+
}
|
|
6857
|
+
setURL(url) {
|
|
6858
|
+
const writable = this.getWritable();
|
|
6859
|
+
writable.__url = url;
|
|
6860
|
+
return writable;
|
|
6861
|
+
}
|
|
6862
|
+
getTarget() {
|
|
6863
|
+
return this.getLatest().__target;
|
|
6864
|
+
}
|
|
6865
|
+
setTarget(target) {
|
|
6866
|
+
const writable = this.getWritable();
|
|
6867
|
+
writable.__target = target;
|
|
6868
|
+
return writable;
|
|
6869
|
+
}
|
|
6870
|
+
getRel() {
|
|
6871
|
+
return this.getLatest().__rel;
|
|
6872
|
+
}
|
|
6873
|
+
setRel(rel) {
|
|
6874
|
+
const writable = this.getWritable();
|
|
6875
|
+
writable.__rel = rel;
|
|
6876
|
+
return writable;
|
|
6877
|
+
}
|
|
6878
|
+
getTitle() {
|
|
6879
|
+
return this.getLatest().__title;
|
|
6880
|
+
}
|
|
6881
|
+
setTitle(title) {
|
|
6882
|
+
const writable = this.getWritable();
|
|
6883
|
+
writable.__title = title;
|
|
6884
|
+
return writable;
|
|
6885
|
+
}
|
|
6886
|
+
insertNewAfter(_, restoreSelection = true) {
|
|
6887
|
+
const linkNode = $createLinkNode(this.__url, {
|
|
6888
|
+
rel: this.__rel,
|
|
6889
|
+
target: this.__target,
|
|
6890
|
+
title: this.__title
|
|
6891
|
+
});
|
|
6892
|
+
this.insertAfter(linkNode, restoreSelection);
|
|
6893
|
+
return linkNode;
|
|
6894
|
+
}
|
|
6895
|
+
canInsertTextBefore() {
|
|
6896
|
+
return false;
|
|
6897
|
+
}
|
|
6898
|
+
canInsertTextAfter() {
|
|
6899
|
+
return false;
|
|
6900
|
+
}
|
|
6901
|
+
canBeEmpty() {
|
|
6902
|
+
return false;
|
|
6903
|
+
}
|
|
6904
|
+
isInline() {
|
|
6905
|
+
return true;
|
|
6906
|
+
}
|
|
6907
|
+
extractWithChild(child, selection) {
|
|
6908
|
+
if (!$isRangeSelection(selection)) return false;
|
|
6909
|
+
const anchorNode = selection.anchor.getNode();
|
|
6910
|
+
const focusNode = selection.focus.getNode();
|
|
6911
|
+
return this.isParentOf(anchorNode) && this.isParentOf(focusNode) && selection.getTextContent().length > 0;
|
|
6912
|
+
}
|
|
6913
|
+
isEmailURI() {
|
|
6914
|
+
return this.__url.startsWith("mailto:");
|
|
6915
|
+
}
|
|
6916
|
+
isWebSiteURI() {
|
|
6917
|
+
return this.__url.startsWith("https://") || this.__url.startsWith("http://");
|
|
6918
|
+
}
|
|
6919
|
+
};
|
|
6920
|
+
function $convertAnchorElement(domNode) {
|
|
6921
|
+
let node = null;
|
|
6922
|
+
if (isHTMLAnchorElement(domNode)) {
|
|
6923
|
+
const content = domNode.textContent;
|
|
6924
|
+
if (content !== null && content !== "" || domNode.children.length > 0) node = $createLinkNode(domNode.getAttribute("href") || "", {
|
|
6925
|
+
rel: domNode.getAttribute("rel"),
|
|
6926
|
+
target: domNode.getAttribute("target"),
|
|
6927
|
+
title: domNode.getAttribute("title")
|
|
6928
|
+
});
|
|
6929
|
+
}
|
|
6930
|
+
return { node };
|
|
6931
|
+
}
|
|
6932
|
+
/**
|
|
6933
|
+
* Takes a URL and creates a LinkNode.
|
|
6934
|
+
* @param url - The URL the LinkNode should direct to.
|
|
6935
|
+
* @param attributes - Optional HTML a tag attributes \\{ target, rel, title \\}
|
|
6936
|
+
* @returns The LinkNode.
|
|
6937
|
+
*/
|
|
6938
|
+
function $createLinkNode(url = "", attributes) {
|
|
6939
|
+
return $applyNodeReplacement(new LinkNode(url, attributes));
|
|
6940
|
+
}
|
|
6941
|
+
/**
|
|
6942
|
+
* Determines if node is a LinkNode.
|
|
6943
|
+
* @param node - The node to be checked.
|
|
6944
|
+
* @returns true if node is a LinkNode, false otherwise.
|
|
6945
|
+
*/
|
|
6946
|
+
function $isLinkNode(node) {
|
|
6947
|
+
return node instanceof LinkNode;
|
|
6948
|
+
}
|
|
6949
|
+
var AutoLinkNode = class AutoLinkNode extends LinkNode {
|
|
6950
|
+
constructor(url = "", attributes = {}, key) {
|
|
6951
|
+
super(url, attributes, key);
|
|
6952
|
+
this.__isUnlinked = attributes.isUnlinked !== void 0 && attributes.isUnlinked !== null ? attributes.isUnlinked : false;
|
|
6953
|
+
}
|
|
6954
|
+
static getType() {
|
|
6955
|
+
return "autolink";
|
|
6956
|
+
}
|
|
6957
|
+
static clone(node) {
|
|
6958
|
+
return new AutoLinkNode(node.__url, {
|
|
6959
|
+
isUnlinked: node.__isUnlinked,
|
|
6960
|
+
rel: node.__rel,
|
|
6961
|
+
target: node.__target,
|
|
6962
|
+
title: node.__title
|
|
6963
|
+
}, node.__key);
|
|
6964
|
+
}
|
|
6965
|
+
getIsUnlinked() {
|
|
6966
|
+
return this.__isUnlinked;
|
|
6967
|
+
}
|
|
6968
|
+
setIsUnlinked(value) {
|
|
6969
|
+
const self = this.getWritable();
|
|
6970
|
+
self.__isUnlinked = value;
|
|
6971
|
+
return self;
|
|
6972
|
+
}
|
|
6973
|
+
createDOM(config, editor) {
|
|
6974
|
+
logger.debug("🔍 config", config);
|
|
6975
|
+
if (this.__isUnlinked) return document.createElement("span");
|
|
6976
|
+
else return super.createDOM(config, editor);
|
|
6977
|
+
}
|
|
6978
|
+
updateDOM(prevNode, anchor, config) {
|
|
6979
|
+
return super.updateDOM(prevNode, anchor, config) || prevNode.__isUnlinked !== this.__isUnlinked;
|
|
6980
|
+
}
|
|
6981
|
+
static importJSON(serializedNode) {
|
|
6982
|
+
return $createAutoLinkNode().updateFromJSON(serializedNode);
|
|
6983
|
+
}
|
|
6984
|
+
updateFromJSON(serializedNode) {
|
|
6985
|
+
return super.updateFromJSON(serializedNode).setIsUnlinked(serializedNode.isUnlinked || false);
|
|
6986
|
+
}
|
|
6987
|
+
static importDOM() {
|
|
6988
|
+
return null;
|
|
6989
|
+
}
|
|
6990
|
+
exportJSON() {
|
|
6991
|
+
return {
|
|
6992
|
+
...super.exportJSON(),
|
|
6993
|
+
isUnlinked: this.__isUnlinked
|
|
6994
|
+
};
|
|
6995
|
+
}
|
|
6996
|
+
insertNewAfter(selection, restoreSelection = true) {
|
|
6997
|
+
const element = this.getParentOrThrow().insertNewAfter(selection, restoreSelection);
|
|
6998
|
+
if ($isElementNode(element)) {
|
|
6999
|
+
const linkNode = $createAutoLinkNode(this.__url, {
|
|
7000
|
+
isUnlinked: this.__isUnlinked,
|
|
7001
|
+
rel: this.__rel,
|
|
7002
|
+
target: this.__target,
|
|
7003
|
+
title: this.__title
|
|
7004
|
+
});
|
|
7005
|
+
element.append(linkNode);
|
|
7006
|
+
return linkNode;
|
|
7007
|
+
}
|
|
7008
|
+
return null;
|
|
7009
|
+
}
|
|
7010
|
+
};
|
|
7011
|
+
/**
|
|
7012
|
+
* Takes a URL and creates an AutoLinkNode. AutoLinkNodes are generally automatically generated
|
|
7013
|
+
* during typing, which is especially useful when a button to generate a LinkNode is not practical.
|
|
7014
|
+
* @param url - The URL the LinkNode should direct to.
|
|
7015
|
+
* @param attributes - Optional HTML a tag attributes. \\{ target, rel, title \\}
|
|
7016
|
+
* @returns The LinkNode.
|
|
7017
|
+
*/
|
|
7018
|
+
function $createAutoLinkNode(url = "", attributes) {
|
|
7019
|
+
return $applyNodeReplacement(new AutoLinkNode(url, attributes));
|
|
7020
|
+
}
|
|
7021
|
+
/**
|
|
7022
|
+
* Determines if node is an AutoLinkNode.
|
|
7023
|
+
* @param node - The node to be checked.
|
|
7024
|
+
* @returns true if node is an AutoLinkNode, false otherwise.
|
|
7025
|
+
*/
|
|
7026
|
+
function $isAutoLinkNode(node) {
|
|
7027
|
+
return node instanceof AutoLinkNode;
|
|
7028
|
+
}
|
|
7029
|
+
const TOGGLE_LINK_COMMAND = createCommand("TOGGLE_LINK_COMMAND");
|
|
7030
|
+
function $getPointNode(point, offset) {
|
|
7031
|
+
if (point.type === "element") {
|
|
7032
|
+
const node = point.getNode();
|
|
7033
|
+
assert($isElementNode(node), "$getPointNode: element point is not an ElementNode");
|
|
7034
|
+
return node.getChildren()[point.offset + offset] || null;
|
|
7035
|
+
}
|
|
7036
|
+
return null;
|
|
7037
|
+
}
|
|
7038
|
+
/**
|
|
7039
|
+
* Preserve the logical start/end of a RangeSelection in situations where
|
|
7040
|
+
* the point is an element that may be reparented in the callback.
|
|
7041
|
+
*
|
|
7042
|
+
* @param $fn The function to run
|
|
7043
|
+
* @returns The result of the callback
|
|
7044
|
+
*/
|
|
7045
|
+
function $withSelectedNodes($fn) {
|
|
7046
|
+
const initialSelection = $getSelection();
|
|
7047
|
+
if (!$isRangeSelection(initialSelection)) return $fn();
|
|
7048
|
+
const normalized = $normalizeSelection__EXPERIMENTAL(initialSelection);
|
|
7049
|
+
const isBackwards = normalized.isBackward();
|
|
7050
|
+
const anchorNode = $getPointNode(normalized.anchor, isBackwards ? -1 : 0);
|
|
7051
|
+
const focusNode = $getPointNode(normalized.focus, isBackwards ? 0 : -1);
|
|
7052
|
+
const rval = $fn();
|
|
7053
|
+
if (anchorNode || focusNode) {
|
|
7054
|
+
const updatedSelection = $getSelection();
|
|
7055
|
+
if ($isRangeSelection(updatedSelection)) {
|
|
7056
|
+
const finalSelection = updatedSelection.clone();
|
|
7057
|
+
if (anchorNode) {
|
|
7058
|
+
const anchorParent = anchorNode.getParent();
|
|
7059
|
+
if (anchorParent) finalSelection.anchor.set(anchorParent.getKey(), anchorNode.getIndexWithinParent() + (isBackwards ? 1 : 0), "element");
|
|
7060
|
+
}
|
|
7061
|
+
if (focusNode) {
|
|
7062
|
+
const focusParent = focusNode.getParent();
|
|
7063
|
+
if (focusParent) finalSelection.focus.set(focusParent.getKey(), focusNode.getIndexWithinParent() + (isBackwards ? 0 : 1), "element");
|
|
7064
|
+
}
|
|
7065
|
+
$setSelection($normalizeSelection__EXPERIMENTAL(finalSelection));
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
return rval;
|
|
7069
|
+
}
|
|
7070
|
+
/**
|
|
7071
|
+
* Generates or updates a LinkNode. It can also delete a LinkNode if the URL is null,
|
|
7072
|
+
* but saves any children and brings them up to the parent node.
|
|
7073
|
+
* @param url - The URL the link directs to.
|
|
7074
|
+
* @param attributes - Optional HTML a tag attributes. \\{ target, rel, title \\}
|
|
7075
|
+
*/
|
|
7076
|
+
function $toggleLink(url, attributes = {}) {
|
|
7077
|
+
const { target, title } = attributes;
|
|
7078
|
+
const rel = attributes.rel === void 0 ? "noreferrer" : attributes.rel;
|
|
7079
|
+
const selection = $getSelection();
|
|
7080
|
+
if (selection === null || !$isRangeSelection(selection) && !$isNodeSelection(selection)) return;
|
|
7081
|
+
if ($isNodeSelection(selection)) {
|
|
7082
|
+
const nodes = selection.getNodes();
|
|
7083
|
+
if (nodes.length === 0) return;
|
|
7084
|
+
nodes.forEach((node) => {
|
|
7085
|
+
if (url === null) {
|
|
7086
|
+
const linkParent = $findMatchingParent(node, (parent) => !$isAutoLinkNode(parent) && $isLinkNode(parent));
|
|
7087
|
+
if (linkParent) {
|
|
7088
|
+
linkParent.insertBefore(node);
|
|
7089
|
+
if (linkParent.getChildren().length === 0) linkParent.remove();
|
|
7090
|
+
}
|
|
7091
|
+
} else {
|
|
7092
|
+
const existingLink = $findMatchingParent(node, (parent) => !$isAutoLinkNode(parent) && $isLinkNode(parent));
|
|
7093
|
+
if (existingLink) {
|
|
7094
|
+
existingLink.setURL(url);
|
|
7095
|
+
if (target !== void 0) existingLink.setTarget(target);
|
|
7096
|
+
if (rel !== void 0) existingLink.setRel(rel);
|
|
7097
|
+
} else {
|
|
7098
|
+
const linkNode = $createLinkNode(url, {
|
|
7099
|
+
rel,
|
|
7100
|
+
target
|
|
7101
|
+
});
|
|
7102
|
+
node.insertBefore(linkNode);
|
|
7103
|
+
linkNode.append(node);
|
|
7104
|
+
}
|
|
7105
|
+
}
|
|
7106
|
+
});
|
|
7107
|
+
return;
|
|
7108
|
+
}
|
|
7109
|
+
const nodes = selection.extract();
|
|
7110
|
+
if (url === null) {
|
|
7111
|
+
nodes.forEach((node) => {
|
|
7112
|
+
const parentLink = $findMatchingParent(node, (parent) => !$isAutoLinkNode(parent) && $isLinkNode(parent));
|
|
7113
|
+
if (parentLink) {
|
|
7114
|
+
const children = parentLink.getChildren();
|
|
7115
|
+
for (const child of children) parentLink.insertBefore(child);
|
|
7116
|
+
parentLink.remove();
|
|
7117
|
+
}
|
|
7118
|
+
});
|
|
7119
|
+
return;
|
|
7120
|
+
}
|
|
7121
|
+
const updatedNodes = /* @__PURE__ */ new Set();
|
|
7122
|
+
const updateLinkNode = (linkNode) => {
|
|
7123
|
+
if (updatedNodes.has(linkNode.getKey())) return;
|
|
7124
|
+
updatedNodes.add(linkNode.getKey());
|
|
7125
|
+
linkNode.setURL(url);
|
|
7126
|
+
if (target !== void 0) linkNode.setTarget(target);
|
|
7127
|
+
if (rel !== void 0) linkNode.setRel(rel);
|
|
7128
|
+
if (title !== void 0) linkNode.setTitle(title);
|
|
7129
|
+
};
|
|
7130
|
+
if (nodes.length === 1) {
|
|
7131
|
+
const firstNode = nodes[0];
|
|
7132
|
+
const linkNode = $getAncestor(firstNode, $isLinkNode);
|
|
7133
|
+
if (linkNode !== null) return updateLinkNode(linkNode);
|
|
7134
|
+
}
|
|
7135
|
+
$withSelectedNodes(() => {
|
|
7136
|
+
let linkNode = null;
|
|
7137
|
+
for (const node of nodes) {
|
|
7138
|
+
if (!node.isAttached()) continue;
|
|
7139
|
+
const parentLinkNode = $getAncestor(node, $isLinkNode);
|
|
7140
|
+
if (parentLinkNode) {
|
|
7141
|
+
updateLinkNode(parentLinkNode);
|
|
7142
|
+
continue;
|
|
7143
|
+
}
|
|
7144
|
+
if ($isElementNode(node)) {
|
|
7145
|
+
if (!node.isInline()) continue;
|
|
7146
|
+
if ($isLinkNode(node)) {
|
|
7147
|
+
if (!$isAutoLinkNode(node) && (linkNode === null || !linkNode.getParentOrThrow().isParentOf(node))) {
|
|
7148
|
+
updateLinkNode(node);
|
|
7149
|
+
linkNode = node;
|
|
7150
|
+
continue;
|
|
7151
|
+
}
|
|
7152
|
+
for (const child of node.getChildren()) node.insertBefore(child);
|
|
7153
|
+
node.remove();
|
|
7154
|
+
continue;
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
const prevLinkNode = node.getPreviousSibling();
|
|
7158
|
+
if ($isLinkNode(prevLinkNode) && prevLinkNode.is(linkNode)) {
|
|
7159
|
+
prevLinkNode.append(node);
|
|
7160
|
+
continue;
|
|
7161
|
+
}
|
|
7162
|
+
linkNode = $createLinkNode(url, {
|
|
7163
|
+
rel,
|
|
7164
|
+
target,
|
|
7165
|
+
title
|
|
7166
|
+
});
|
|
7167
|
+
node.insertAfter(linkNode);
|
|
7168
|
+
linkNode.append(node);
|
|
7169
|
+
}
|
|
7170
|
+
});
|
|
7171
|
+
}
|
|
7172
|
+
function $getAncestor(node, predicate) {
|
|
7173
|
+
let parent = node;
|
|
7174
|
+
while (parent !== null && parent.getParent() !== null && !predicate(parent)) parent = parent.getParentOrThrow();
|
|
7175
|
+
return predicate(parent) ? parent : null;
|
|
7176
|
+
}
|
|
7177
|
+
const PHONE_NUMBER_REGEX = /^\+?[\d\s()-]{5,}$/;
|
|
7178
|
+
/**
|
|
7179
|
+
* Formats a URL string by adding appropriate protocol if missing
|
|
7180
|
+
*
|
|
7181
|
+
* @param url - URL to format
|
|
7182
|
+
* @returns Formatted URL with appropriate protocol
|
|
7183
|
+
*/
|
|
7184
|
+
function formatUrl(url) {
|
|
7185
|
+
if (/^[a-z][\d+.a-z-]*:/i.test(url)) return url;
|
|
7186
|
+
else if (/^[#./]/.test(url)) return url;
|
|
7187
|
+
else if (url.includes("@")) return `mailto:${url}`;
|
|
7188
|
+
else if (PHONE_NUMBER_REGEX.test(url)) return `tel:${url}`;
|
|
7189
|
+
return url;
|
|
7190
|
+
}
|
|
7191
|
+
//#endregion
|
|
7192
|
+
//#region src/plugins/link/command/index.ts
|
|
7193
|
+
const INSERT_LINK_COMMAND = createCommand("INSERT_LINK_COMMAND");
|
|
7194
|
+
const UPDATE_LINK_TEXT_COMMAND = createCommand("UPDATE_LINK_TEXT_COMMAND");
|
|
7195
|
+
function registerLinkCommand(editor) {
|
|
7196
|
+
return mergeRegister(editor.registerCommand(INSERT_LINK_COMMAND, (payload) => {
|
|
7197
|
+
const { url, title = url } = payload;
|
|
7198
|
+
editor.update(() => {
|
|
7199
|
+
const linkNode = $createLinkNode(url, { title });
|
|
7200
|
+
const textNode = $createTextNode(title);
|
|
7201
|
+
linkNode.append(textNode);
|
|
7202
|
+
$insertNodes([linkNode]);
|
|
7203
|
+
});
|
|
7204
|
+
return false;
|
|
7205
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(UPDATE_LINK_TEXT_COMMAND, (payload) => {
|
|
7206
|
+
const { key, text } = payload;
|
|
7207
|
+
editor.update(() => {
|
|
7208
|
+
const linkNode = $getNodeByKey(key);
|
|
7209
|
+
if (linkNode) {
|
|
7210
|
+
const newLinkNode = $createLinkNode(linkNode.getURL(), { title: text });
|
|
7211
|
+
const textNode = $createTextNode(text);
|
|
7212
|
+
newLinkNode.append(textNode);
|
|
7213
|
+
linkNode?.replace(newLinkNode);
|
|
7214
|
+
newLinkNode.select(1);
|
|
7215
|
+
}
|
|
7216
|
+
});
|
|
7217
|
+
return false;
|
|
7218
|
+
}, COMMAND_PRIORITY_EDITOR));
|
|
7219
|
+
}
|
|
7220
|
+
//#endregion
|
|
7221
|
+
//#region src/plugins/link/service/i-link-service.ts
|
|
7222
|
+
const ILinkService = genServiceId("LinkService");
|
|
7223
|
+
var LinkService = class extends EventEmitter {
|
|
7224
|
+
constructor(..._args) {
|
|
7225
|
+
super(..._args);
|
|
7226
|
+
this._enableLinkToolbar = true;
|
|
7227
|
+
}
|
|
7228
|
+
get enableLinkToolbar() {
|
|
7229
|
+
return this._enableLinkToolbar;
|
|
7230
|
+
}
|
|
7231
|
+
setLinkToolbar(enable) {
|
|
7232
|
+
this._enableLinkToolbar = enable;
|
|
7233
|
+
this.emit("linkToolbarChange", enable);
|
|
7234
|
+
}
|
|
7235
|
+
};
|
|
7236
|
+
//#endregion
|
|
7237
|
+
//#region src/plugins/link/utils/index.ts
|
|
7238
|
+
const SUPPORTED_URL_PROTOCOLS = new Set([
|
|
7239
|
+
"http:",
|
|
7240
|
+
"https:",
|
|
7241
|
+
"mailto:",
|
|
7242
|
+
"sms:",
|
|
7243
|
+
"tel:"
|
|
7244
|
+
]);
|
|
7245
|
+
function sanitizeUrl(url) {
|
|
7246
|
+
try {
|
|
7247
|
+
const parsedUrl = new URL(url);
|
|
7248
|
+
if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) return "about:blank";
|
|
7249
|
+
} catch {
|
|
7250
|
+
return url;
|
|
7251
|
+
}
|
|
7252
|
+
return url;
|
|
7253
|
+
}
|
|
7254
|
+
const urlRegExp = /* @__PURE__ */ new RegExp(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\w$&+,:;=-]+@)?[\d.A-Za-z-]+|(?:www.|[\w$&+,:;=-]+@)[\d.A-Za-z-]+)((?:\/[%+./~\w-_]*)?\??[\w%&+.;=@-]*#?\w*)?)/);
|
|
7255
|
+
function extractUrlFromText(text) {
|
|
7256
|
+
const match = urlRegExp.exec(text);
|
|
7257
|
+
if (!match) return null;
|
|
7258
|
+
const raw = match[0];
|
|
7259
|
+
const start = match.index ?? text.indexOf(raw);
|
|
7260
|
+
const trimmed = raw.replace(/[)\],.;:]+$/u, "");
|
|
7261
|
+
return {
|
|
7262
|
+
index: start,
|
|
7263
|
+
length: trimmed.length,
|
|
7264
|
+
url: trimmed
|
|
7265
|
+
};
|
|
7266
|
+
}
|
|
7267
|
+
function getSelectedNode(selection) {
|
|
7268
|
+
const anchor = selection.anchor;
|
|
7269
|
+
const focus = selection.focus;
|
|
7270
|
+
const anchorNode = selection.anchor.getNode();
|
|
7271
|
+
const focusNode = selection.focus.getNode();
|
|
7272
|
+
if (anchorNode === focusNode) return anchorNode;
|
|
7273
|
+
if (selection.isBackward()) return $isAtNodeEnd(focus) ? anchorNode : focusNode;
|
|
7274
|
+
else return $isAtNodeEnd(anchor) ? anchorNode : focusNode;
|
|
7275
|
+
}
|
|
7276
|
+
//#endregion
|
|
7277
|
+
//#region src/plugins/link/plugin/registry.ts
|
|
7278
|
+
function registerLinkCommands(editor, kernel, options) {
|
|
7279
|
+
const { validateUrl, attributes, enableHotkey = true } = options || {};
|
|
7280
|
+
const state = { isLink: false };
|
|
7281
|
+
const registrations = [editor.registerUpdateListener(() => {
|
|
7282
|
+
const selection = editor.getEditorState().read(() => $getSelection());
|
|
7283
|
+
if (!selection) return;
|
|
7284
|
+
if ($isRangeSelection(selection)) editor.getEditorState().read(() => {
|
|
7285
|
+
const node = getSelectedNode(selection);
|
|
7286
|
+
state.isLink = $isLinkNode(node.getParent()) || $isLinkNode(node);
|
|
7287
|
+
});
|
|
7288
|
+
else state.isLink = false;
|
|
7289
|
+
}), editor.registerCommand(TOGGLE_LINK_COMMAND, (payload) => {
|
|
7290
|
+
if (payload === null) {
|
|
7291
|
+
$toggleLink(payload);
|
|
7292
|
+
return true;
|
|
7293
|
+
} else if (typeof payload === "string") {
|
|
7294
|
+
if (validateUrl === void 0 || validateUrl(payload)) {
|
|
7295
|
+
$toggleLink(payload, attributes);
|
|
7296
|
+
return true;
|
|
7297
|
+
}
|
|
7298
|
+
return false;
|
|
7299
|
+
} else {
|
|
7300
|
+
const { url, target, rel, title } = payload;
|
|
7301
|
+
$toggleLink(url, {
|
|
7302
|
+
...attributes,
|
|
7303
|
+
rel,
|
|
7304
|
+
target,
|
|
7305
|
+
title
|
|
7306
|
+
});
|
|
7307
|
+
return true;
|
|
7308
|
+
}
|
|
7309
|
+
}, COMMAND_PRIORITY_LOW)];
|
|
7310
|
+
registrations.push(kernel.registerHotkey(HotkeyEnum.Link, () => {
|
|
7311
|
+
if (state.isLink) {
|
|
7312
|
+
editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
|
|
7313
|
+
return;
|
|
7314
|
+
}
|
|
7315
|
+
let nextUrl = sanitizeUrl("https://");
|
|
7316
|
+
let expandTo = null;
|
|
7317
|
+
editor.getEditorState().read(() => {
|
|
7318
|
+
const selection = $getSelection();
|
|
7319
|
+
if ($isRangeSelection(selection)) if (!selection.isCollapsed()) {
|
|
7320
|
+
const maybeUrl = formatUrl(selection.getTextContent().trim());
|
|
7321
|
+
if (validateUrl?.(maybeUrl)) nextUrl = maybeUrl;
|
|
7322
|
+
} else {
|
|
7323
|
+
const found = extractUrlFromText(selection.anchor.getNode().getTextContent());
|
|
7324
|
+
if (found && validateUrl?.(formatUrl(found.url))) {
|
|
7325
|
+
nextUrl = formatUrl(found.url);
|
|
7326
|
+
expandTo = {
|
|
7327
|
+
index: found.index,
|
|
7328
|
+
length: found.length
|
|
7329
|
+
};
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
});
|
|
7333
|
+
editor.update(() => {
|
|
7334
|
+
if (expandTo) {
|
|
7335
|
+
const selection = $getSelection();
|
|
7336
|
+
if ($isRangeSelection(selection)) {
|
|
7337
|
+
const anchorNode = selection.anchor.getNode();
|
|
7338
|
+
selection.anchor.set(anchorNode.getKey(), expandTo.index, "text");
|
|
7339
|
+
selection.focus.set(anchorNode.getKey(), expandTo.index + expandTo.length, "text");
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
editor.dispatchCommand(TOGGLE_LINK_COMMAND, nextUrl);
|
|
7343
|
+
});
|
|
7344
|
+
}, {
|
|
7345
|
+
enabled: enableHotkey,
|
|
7346
|
+
preventDefault: true,
|
|
7347
|
+
stopPropagation: true
|
|
7348
|
+
}));
|
|
7349
|
+
return mergeRegister(...registrations);
|
|
7350
|
+
}
|
|
7351
|
+
//#endregion
|
|
7352
|
+
//#region src/plugins/link/plugin/index.ts
|
|
7353
|
+
const LinkPlugin = class extends KernelPlugin {
|
|
7354
|
+
static {
|
|
7355
|
+
this.pluginName = "LinkPlugin";
|
|
7356
|
+
}
|
|
7357
|
+
constructor(kernel, config) {
|
|
7358
|
+
super();
|
|
7359
|
+
this.kernel = kernel;
|
|
7360
|
+
this.config = config;
|
|
7361
|
+
this.linkRegex = /^https?:\/\/\S+$/;
|
|
7362
|
+
this.service = new LinkService();
|
|
7363
|
+
kernel.registerNodes([LinkNode, AutoLinkNode]);
|
|
7364
|
+
kernel.registerService(ILinkService, this.service);
|
|
7365
|
+
if (config?.theme) kernel.registerThemes(config.theme);
|
|
7366
|
+
if (config?.linkRegex) this.linkRegex = config.linkRegex;
|
|
7367
|
+
}
|
|
7368
|
+
onInit(editor) {
|
|
7369
|
+
this.register(registerLinkCommand(editor));
|
|
7370
|
+
this.register(registerLinkCommands(editor, this.kernel, {
|
|
7371
|
+
attributes: this.config?.attributes,
|
|
7372
|
+
enableHotkey: this.config?.enableHotkey,
|
|
7373
|
+
validateUrl: this.config?.validateUrl
|
|
7374
|
+
}));
|
|
7375
|
+
this.register(editor.registerCommand(PASTE_COMMAND, (payload) => {
|
|
7376
|
+
const { clipboardData } = payload;
|
|
7377
|
+
if (clipboardData && clipboardData.types && clipboardData.types.length === 1 && clipboardData.types[0] === "text/plain") {
|
|
7378
|
+
const data = clipboardData.getData("text/plain").trim();
|
|
7379
|
+
if (this.linkRegex.test(data)) {
|
|
7380
|
+
payload.stopImmediatePropagation();
|
|
7381
|
+
payload.preventDefault();
|
|
7382
|
+
editor.dispatchCommand(INSERT_LINK_COMMAND, { url: data });
|
|
7383
|
+
return true;
|
|
7384
|
+
}
|
|
7385
|
+
}
|
|
7386
|
+
return false;
|
|
7387
|
+
}, COMMAND_PRIORITY_NORMAL));
|
|
7388
|
+
this.registerMarkdown();
|
|
7389
|
+
this.registerLiteXml();
|
|
7390
|
+
}
|
|
7391
|
+
registerLiteXml() {
|
|
7392
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
7393
|
+
if (!litexmlService) return;
|
|
7394
|
+
litexmlService.registerXMLWriter(LinkNode.getType(), (node, ctx) => {
|
|
7395
|
+
if ($isLinkNode(node)) {
|
|
7396
|
+
const attributes = { href: node.getURL() };
|
|
7397
|
+
return ctx.createXmlNode("a", attributes);
|
|
7398
|
+
}
|
|
7399
|
+
return false;
|
|
7400
|
+
});
|
|
7401
|
+
litexmlService.registerXMLReader("a", (xmlNode, children) => {
|
|
7402
|
+
return [INodeHelper.createElementNode("link", {
|
|
7403
|
+
children,
|
|
7404
|
+
direction: "ltr",
|
|
7405
|
+
format: "",
|
|
7406
|
+
indent: 0,
|
|
7407
|
+
type: "link",
|
|
7408
|
+
url: xmlNode.getAttribute("href") || "",
|
|
7409
|
+
version: 1
|
|
7410
|
+
})];
|
|
7411
|
+
});
|
|
7412
|
+
}
|
|
7413
|
+
registerMarkdown() {
|
|
7414
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownShortCut({
|
|
7415
|
+
regExp: /\[([^[]+)]\(([^\s()]+)(?:\s"((?:[^"]*\\")*[^"]*)"\s*)?\)\s?$/,
|
|
7416
|
+
replace: (textNode, match) => {
|
|
7417
|
+
const [, linkText, linkUrl, linkTitle] = match;
|
|
7418
|
+
const linkNode = $createLinkNode(linkUrl, { title: linkTitle });
|
|
7419
|
+
const linkTextNode = $createTextNode(linkText);
|
|
7420
|
+
linkTextNode.setFormat(textNode.getFormat());
|
|
7421
|
+
linkNode.append(linkTextNode);
|
|
7422
|
+
textNode.replace(linkNode);
|
|
7423
|
+
return linkTextNode;
|
|
7424
|
+
},
|
|
7425
|
+
trigger: ")",
|
|
7426
|
+
type: "text-match"
|
|
5949
7427
|
});
|
|
5950
7428
|
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(LinkNode.getType(), (ctx, node) => {
|
|
5951
7429
|
if ($isLinkNode(node)) ctx.wrap("[", `](${node.getURL()})`);
|
|
5952
7430
|
});
|
|
5953
|
-
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("link", (node, children) => {
|
|
5954
|
-
return [INodeHelper.createElementNode("link", {
|
|
7431
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("link", (node, children) => {
|
|
7432
|
+
return [INodeHelper.createElementNode("link", {
|
|
7433
|
+
children,
|
|
7434
|
+
direction: "ltr",
|
|
7435
|
+
format: "",
|
|
7436
|
+
indent: 0,
|
|
7437
|
+
title: node.title || void 0,
|
|
7438
|
+
type: "link",
|
|
7439
|
+
url: node.url || "",
|
|
7440
|
+
version: 1
|
|
7441
|
+
})];
|
|
7442
|
+
});
|
|
7443
|
+
}
|
|
7444
|
+
};
|
|
7445
|
+
//#endregion
|
|
7446
|
+
//#region src/utils/cx.ts
|
|
7447
|
+
const cx = (...classNames) => classNames.filter(Boolean).join(" ");
|
|
7448
|
+
//#endregion
|
|
7449
|
+
//#region src/plugins/list/utils/index.ts
|
|
7450
|
+
const LIST_INDENT_SIZE = 4;
|
|
7451
|
+
function getIndent(whitespaces) {
|
|
7452
|
+
const tabs = whitespaces.match(/\t/g);
|
|
7453
|
+
const spaces = whitespaces.match(/ /g);
|
|
7454
|
+
let indent = 0;
|
|
7455
|
+
if (tabs) indent += tabs.length;
|
|
7456
|
+
if (spaces) indent += Math.floor(spaces.length / LIST_INDENT_SIZE);
|
|
7457
|
+
return indent;
|
|
7458
|
+
}
|
|
7459
|
+
const listReplace = (listType) => {
|
|
7460
|
+
return (parentNode, children, match, isImport) => {
|
|
7461
|
+
const previousNode = parentNode.getPreviousSibling();
|
|
7462
|
+
const nextNode = parentNode.getNextSibling();
|
|
7463
|
+
const listItem = $createListItemNode(listType === "check" ? match[3] === "x" : void 0);
|
|
7464
|
+
if ($isListNode(nextNode) && nextNode.getListType() === listType) {
|
|
7465
|
+
const firstChild = nextNode.getFirstChild();
|
|
7466
|
+
if (firstChild !== null) firstChild.insertBefore(listItem);
|
|
7467
|
+
else nextNode.append(listItem);
|
|
7468
|
+
parentNode.remove();
|
|
7469
|
+
} else if ($isListNode(previousNode) && previousNode.getListType() === listType) {
|
|
7470
|
+
previousNode.append(listItem);
|
|
7471
|
+
parentNode.remove();
|
|
7472
|
+
} else {
|
|
7473
|
+
const list = $createListNode(listType, listType === "number" ? Number(match[2]) : void 0);
|
|
7474
|
+
list.append(listItem);
|
|
7475
|
+
parentNode.replace(list);
|
|
7476
|
+
}
|
|
7477
|
+
listItem.append(...children);
|
|
7478
|
+
if (!isImport) listItem.select(0, 0);
|
|
7479
|
+
const indent = getIndent(match[1]);
|
|
7480
|
+
if (indent) listItem.setIndent(indent);
|
|
7481
|
+
};
|
|
7482
|
+
};
|
|
7483
|
+
function $indentOverTab(selection) {
|
|
7484
|
+
if ($filter(selection.getNodes(), (node) => {
|
|
7485
|
+
if ($isBlockElementNode(node) && node.canIndent()) return node;
|
|
7486
|
+
return null;
|
|
7487
|
+
}).length > 0) return true;
|
|
7488
|
+
const anchor = selection.anchor;
|
|
7489
|
+
const focus = selection.focus;
|
|
7490
|
+
const first = focus.isBefore(anchor) ? focus : anchor;
|
|
7491
|
+
const firstBlock = $getNearestBlockElementAncestorOrThrow(first.getNode());
|
|
7492
|
+
if (firstBlock.canIndent()) {
|
|
7493
|
+
const firstBlockKey = firstBlock.getKey();
|
|
7494
|
+
let selectionAtStart = $createRangeSelection();
|
|
7495
|
+
selectionAtStart.anchor.set(firstBlockKey, 0, "element");
|
|
7496
|
+
selectionAtStart.focus.set(firstBlockKey, 0, "element");
|
|
7497
|
+
selectionAtStart = $normalizeSelection__EXPERIMENTAL(selectionAtStart);
|
|
7498
|
+
if (selectionAtStart.anchor.is(first)) return true;
|
|
7499
|
+
}
|
|
7500
|
+
return false;
|
|
7501
|
+
}
|
|
7502
|
+
//#endregion
|
|
7503
|
+
//#region src/plugins/list/plugin/checkList.ts
|
|
7504
|
+
/**
|
|
7505
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
7506
|
+
*
|
|
7507
|
+
* This source code is licensed under the MIT license found in the
|
|
7508
|
+
* LICENSE file in the root directory of this source tree.
|
|
7509
|
+
*
|
|
7510
|
+
*/
|
|
7511
|
+
const INSERT_CHECK_LIST_COMMAND = createCommand("INSERT_CHECK_LIST_COMMAND");
|
|
7512
|
+
function isHeadlessEditor$1(editor) {
|
|
7513
|
+
return editor._headless === true;
|
|
7514
|
+
}
|
|
7515
|
+
function handleCheckItemEvent(event, callback) {
|
|
7516
|
+
const target = event.target;
|
|
7517
|
+
if (!isHTMLElement$1(target)) return;
|
|
7518
|
+
const firstChild = target.firstChild;
|
|
7519
|
+
if (isHTMLElement$1(firstChild) && (firstChild.tagName === "UL" || firstChild.tagName === "OL")) return;
|
|
7520
|
+
const parentNode = target.parentNode;
|
|
7521
|
+
if (!parentNode || parentNode.__lexicalListType !== "check") return;
|
|
7522
|
+
const rect = target.getBoundingClientRect();
|
|
7523
|
+
const zoom = calculateZoomLevel(target);
|
|
7524
|
+
const clientX = event.clientX / zoom;
|
|
7525
|
+
const beforeStyles = window.getComputedStyle ? window.getComputedStyle(target, "::before") : { width: "0px" };
|
|
7526
|
+
const beforeWidthInPixels = parseFloat(beforeStyles.width);
|
|
7527
|
+
const beforeMargin = parseFloat(beforeStyles.marginLeft);
|
|
7528
|
+
const clickAreaPadding = event.pointerType === "touch" ? 32 : 0;
|
|
7529
|
+
if (target.dir === "rtl" ? clientX < rect.right + clickAreaPadding && clientX > rect.right - beforeWidthInPixels - clickAreaPadding : clientX > rect.left + beforeMargin - clickAreaPadding && clientX < rect.left + beforeMargin + beforeWidthInPixels + clickAreaPadding) callback();
|
|
7530
|
+
}
|
|
7531
|
+
function handleClick(event) {
|
|
7532
|
+
handleCheckItemEvent(event, () => {
|
|
7533
|
+
if (isHTMLElement$1(event.target)) {
|
|
7534
|
+
const domNode = event.target;
|
|
7535
|
+
const editor = getNearestEditorFromDOMNode(domNode);
|
|
7536
|
+
if (editor && editor.isEditable()) editor.update(() => {
|
|
7537
|
+
const node = $getNearestNodeFromDOMNode(domNode);
|
|
7538
|
+
if ($isListItemNode(node)) {
|
|
7539
|
+
domNode.focus();
|
|
7540
|
+
node.toggleChecked();
|
|
7541
|
+
}
|
|
7542
|
+
});
|
|
7543
|
+
}
|
|
7544
|
+
});
|
|
7545
|
+
}
|
|
7546
|
+
function handlePointerDown(event) {
|
|
7547
|
+
handleCheckItemEvent(event, () => {
|
|
7548
|
+
event.preventDefault();
|
|
7549
|
+
});
|
|
7550
|
+
}
|
|
7551
|
+
function getActiveCheckListItem() {
|
|
7552
|
+
const activeElement = document.activeElement;
|
|
7553
|
+
return isHTMLElement$1(activeElement) && activeElement.tagName === "LI" && activeElement.parentNode && activeElement.parentNode.__lexicalListType === "check" ? activeElement : null;
|
|
7554
|
+
}
|
|
7555
|
+
function findCheckListItemSibling(node, backward) {
|
|
7556
|
+
let sibling = backward ? node.getPreviousSibling() : node.getNextSibling();
|
|
7557
|
+
let parent = node;
|
|
7558
|
+
while (!sibling && $isListItemNode(parent)) {
|
|
7559
|
+
parent = parent.getParentOrThrow().getParent();
|
|
7560
|
+
if (parent) sibling = backward ? parent.getPreviousSibling() : parent.getNextSibling();
|
|
7561
|
+
}
|
|
7562
|
+
while ($isListItemNode(sibling)) {
|
|
7563
|
+
const firstChild = backward ? sibling.getLastChild() : sibling.getFirstChild();
|
|
7564
|
+
if (!$isListNode(firstChild)) return sibling;
|
|
7565
|
+
sibling = backward ? firstChild.getLastChild() : firstChild.getFirstChild();
|
|
7566
|
+
}
|
|
7567
|
+
return null;
|
|
7568
|
+
}
|
|
7569
|
+
function handleArrowUpOrDown(event, editor, backward) {
|
|
7570
|
+
const activeItem = getActiveCheckListItem();
|
|
7571
|
+
if (activeItem) editor.update(() => {
|
|
7572
|
+
const listItem = $getNearestNodeFromDOMNode(activeItem);
|
|
7573
|
+
if (!$isListItemNode(listItem)) return;
|
|
7574
|
+
const nextListItem = findCheckListItemSibling(listItem, backward);
|
|
7575
|
+
if (nextListItem) {
|
|
7576
|
+
nextListItem.selectStart();
|
|
7577
|
+
const dom = editor.getElementByKey(nextListItem.__key);
|
|
7578
|
+
if (dom) {
|
|
7579
|
+
event.preventDefault();
|
|
7580
|
+
setTimeout(() => {
|
|
7581
|
+
dom.focus();
|
|
7582
|
+
}, 0);
|
|
7583
|
+
}
|
|
7584
|
+
}
|
|
7585
|
+
});
|
|
7586
|
+
return false;
|
|
7587
|
+
}
|
|
7588
|
+
function registerCheckList(editor) {
|
|
7589
|
+
const registrations = [
|
|
7590
|
+
editor.registerCommand(INSERT_CHECK_LIST_COMMAND, () => {
|
|
7591
|
+
$insertList("check");
|
|
7592
|
+
return true;
|
|
7593
|
+
}, COMMAND_PRIORITY_LOW),
|
|
7594
|
+
editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event) => {
|
|
7595
|
+
return handleArrowUpOrDown(event, editor, false);
|
|
7596
|
+
}, COMMAND_PRIORITY_LOW),
|
|
7597
|
+
editor.registerCommand(KEY_ARROW_UP_COMMAND, (event) => {
|
|
7598
|
+
return handleArrowUpOrDown(event, editor, true);
|
|
7599
|
+
}, COMMAND_PRIORITY_LOW),
|
|
7600
|
+
editor.registerCommand(KEY_ARROW_LEFT_COMMAND, (event) => {
|
|
7601
|
+
return editor.getEditorState().read(() => {
|
|
7602
|
+
const selection = $getSelection();
|
|
7603
|
+
if ($isRangeSelection(selection) && selection.isCollapsed()) {
|
|
7604
|
+
const { anchor } = selection;
|
|
7605
|
+
const isElement = anchor.type === "element";
|
|
7606
|
+
if (isElement || anchor.offset === 0) {
|
|
7607
|
+
const anchorNode = anchor.getNode();
|
|
7608
|
+
const elementNode = $findMatchingParent(anchorNode, (node) => $isElementNode(node) && !node.isInline());
|
|
7609
|
+
if ($isListItemNode(elementNode)) {
|
|
7610
|
+
const parent = elementNode.getParent();
|
|
7611
|
+
if ($isListNode(parent) && parent.getListType() === "check" && (isElement || elementNode.getFirstDescendant() === anchorNode)) {
|
|
7612
|
+
const domNode = editor.getElementByKey(elementNode.__key);
|
|
7613
|
+
if (domNode && document.activeElement !== domNode) {
|
|
7614
|
+
domNode.focus();
|
|
7615
|
+
event.preventDefault();
|
|
7616
|
+
return true;
|
|
7617
|
+
}
|
|
7618
|
+
}
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7621
|
+
}
|
|
7622
|
+
return false;
|
|
7623
|
+
});
|
|
7624
|
+
}, COMMAND_PRIORITY_LOW)
|
|
7625
|
+
];
|
|
7626
|
+
if (!isHeadlessEditor$1(editor)) registrations.push(editor.registerRootListener((rootElement, prevElement) => {
|
|
7627
|
+
if (rootElement !== null) {
|
|
7628
|
+
rootElement.addEventListener("click", handleClick);
|
|
7629
|
+
rootElement.addEventListener("pointerdown", handlePointerDown);
|
|
7630
|
+
}
|
|
7631
|
+
if (prevElement !== null) {
|
|
7632
|
+
prevElement.removeEventListener("click", handleClick);
|
|
7633
|
+
prevElement.removeEventListener("pointerdown", handlePointerDown);
|
|
7634
|
+
}
|
|
7635
|
+
}));
|
|
7636
|
+
return mergeRegister(...registrations);
|
|
7637
|
+
}
|
|
7638
|
+
//#endregion
|
|
7639
|
+
//#region src/plugins/list/plugin/registry.ts
|
|
7640
|
+
function registerListCommands(editor, kernel, options) {
|
|
7641
|
+
const { enableHotkey = true } = options || {};
|
|
7642
|
+
return mergeRegister(kernel.registerHotkey(HotkeyEnum.BulletList, () => editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0), {
|
|
7643
|
+
enabled: enableHotkey,
|
|
7644
|
+
preventDefault: true,
|
|
7645
|
+
stopImmediatePropagation: true
|
|
7646
|
+
}), kernel.registerHotkey(HotkeyEnum.NumberList, () => editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0), {
|
|
7647
|
+
enabled: enableHotkey,
|
|
7648
|
+
preventDefault: true,
|
|
7649
|
+
stopImmediatePropagation: true
|
|
7650
|
+
}), editor.registerCommand(KEY_TAB_COMMAND, (event) => {
|
|
7651
|
+
const selection = $getSelection();
|
|
7652
|
+
if (!$isRangeSelection(selection)) return false;
|
|
7653
|
+
event.preventDefault();
|
|
7654
|
+
const command = $indentOverTab(selection) ? event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND : INSERT_TAB_COMMAND;
|
|
7655
|
+
return editor.dispatchCommand(command, void 0);
|
|
7656
|
+
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_BACKSPACE_COMMAND, (event) => {
|
|
7657
|
+
const selection = $getSelection();
|
|
7658
|
+
if (!$isRangeSelection(selection) || !selection.isCollapsed()) return false;
|
|
7659
|
+
const anchor = selection.anchor;
|
|
7660
|
+
if (anchor.offset !== 0) return false;
|
|
7661
|
+
const anchorNode = anchor.getNode();
|
|
7662
|
+
let listItemNode;
|
|
7663
|
+
if ($isListItemNode(anchorNode)) listItemNode = anchorNode;
|
|
7664
|
+
else if ($isTextNode(anchorNode)) {
|
|
7665
|
+
if (anchorNode.getPreviousSibling()) return false;
|
|
7666
|
+
const parent = anchorNode.getParentOrThrow();
|
|
7667
|
+
if (!$isListItemNode(parent)) return false;
|
|
7668
|
+
listItemNode = parent;
|
|
7669
|
+
}
|
|
7670
|
+
if (!listItemNode || !$isRootNode(listItemNode.getParent()?.getParent())) return false;
|
|
7671
|
+
const listNode = listItemNode.getParentOrThrow();
|
|
7672
|
+
queueMicrotask(() => {
|
|
7673
|
+
editor.update(() => {
|
|
7674
|
+
if (!listItemNode) return;
|
|
7675
|
+
let newlistNode;
|
|
7676
|
+
if (listItemNode.getPreviousSibling() === null) {
|
|
7677
|
+
listItemNode.replace($createParagraphNode(), true).select(0, 0);
|
|
7678
|
+
return;
|
|
7679
|
+
}
|
|
7680
|
+
let next = listItemNode.getNextSibling();
|
|
7681
|
+
if (next) newlistNode = $createListNode(listNode.getListType(), listItemNode.getValue());
|
|
7682
|
+
while (next && newlistNode) {
|
|
7683
|
+
next.remove();
|
|
7684
|
+
newlistNode.append(next);
|
|
7685
|
+
next = next.getNextSibling();
|
|
7686
|
+
}
|
|
7687
|
+
const p = listItemNode.replace($createParagraphNode(), true);
|
|
7688
|
+
p.remove();
|
|
7689
|
+
listNode.insertAfter(p);
|
|
7690
|
+
if (newlistNode) p.insertAfter(newlistNode);
|
|
7691
|
+
p.select(0, 0);
|
|
7692
|
+
});
|
|
7693
|
+
});
|
|
7694
|
+
event.stopImmediatePropagation();
|
|
7695
|
+
event.preventDefault();
|
|
7696
|
+
return true;
|
|
7697
|
+
}, COMMAND_PRIORITY_LOW));
|
|
7698
|
+
}
|
|
7699
|
+
//#endregion
|
|
7700
|
+
//#region src/plugins/list/plugin/index.ts
|
|
7701
|
+
const ORDERED_LIST_REGEX = /^(\s*)(\d+)\.\s/;
|
|
7702
|
+
const UNORDERED_LIST_REGEX = /^(\s*)[*+-]\s/;
|
|
7703
|
+
const CHECK_LIST_REGEX = /^(\s*)(?:-\s)?\s?(\[(\s|x)?])\s/i;
|
|
7704
|
+
const ListPlugin = class extends KernelPlugin {
|
|
7705
|
+
static {
|
|
7706
|
+
this.pluginName = "ListPlugin";
|
|
7707
|
+
}
|
|
7708
|
+
constructor(kernel, config) {
|
|
7709
|
+
super();
|
|
7710
|
+
this.kernel = kernel;
|
|
7711
|
+
this.config = config;
|
|
7712
|
+
kernel.registerNodes([ListNode, ListItemNode]);
|
|
7713
|
+
kernel.registerThemes({ list: {
|
|
7714
|
+
checklist: "editor_listItemCheck",
|
|
7715
|
+
listitem: "editor_listItem",
|
|
7716
|
+
listitemChecked: "editor_listItemChecked",
|
|
7717
|
+
listitemUnchecked: "editor_listItemUnchecked",
|
|
7718
|
+
nested: { listitem: "editor_listItemNested" },
|
|
7719
|
+
ol: cx("editor_listOrdered", config?.theme),
|
|
7720
|
+
olDepth: [
|
|
7721
|
+
"editor_listOrdered dp1",
|
|
7722
|
+
"editor_listOrdered dp2",
|
|
7723
|
+
"editor_listOrdered dp3",
|
|
7724
|
+
"editor_listOrdered dp4",
|
|
7725
|
+
"editor_listOrdered dp5"
|
|
7726
|
+
],
|
|
7727
|
+
ul: cx("editor_listUnordered", config?.theme)
|
|
7728
|
+
} });
|
|
7729
|
+
}
|
|
7730
|
+
onInit(editor) {
|
|
7731
|
+
this.register(registerList(editor));
|
|
7732
|
+
this.register(registerCheckList(editor));
|
|
7733
|
+
this.register(registerListStrictIndentTransform(editor));
|
|
7734
|
+
this.register(registerListCommands(editor, this.kernel, { enableHotkey: this.config?.enableHotkey }));
|
|
7735
|
+
this.registerMarkdown();
|
|
7736
|
+
this.registerLiteXml();
|
|
7737
|
+
}
|
|
7738
|
+
registerLiteXml() {
|
|
7739
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
7740
|
+
if (!litexmlService) return;
|
|
7741
|
+
litexmlService.registerXMLWriter(ListNode.getType(), (node, ctx) => {
|
|
7742
|
+
if ($isListNode(node)) {
|
|
7743
|
+
const tagName = node.getListType() === "number" ? "ol" : "ul";
|
|
7744
|
+
const attributes = {};
|
|
7745
|
+
if (node.getListType() === "number" && node.getStart() !== 1) attributes.start = node.getStart().toString();
|
|
7746
|
+
return ctx.createXmlNode(tagName, attributes);
|
|
7747
|
+
}
|
|
7748
|
+
return false;
|
|
7749
|
+
});
|
|
7750
|
+
litexmlService.registerXMLWriter(ListItemNode.getType(), (node, ctx) => {
|
|
7751
|
+
if ($isListItemNode(node)) return ctx.createXmlNode("li");
|
|
7752
|
+
return false;
|
|
7753
|
+
});
|
|
7754
|
+
litexmlService.registerXMLReader("ol", (xmlNode, children) => {
|
|
7755
|
+
return INodeHelper.createElementNode("list", {
|
|
7756
|
+
children,
|
|
7757
|
+
direction: "ltr",
|
|
7758
|
+
format: "",
|
|
7759
|
+
indent: 0,
|
|
7760
|
+
listType: "number",
|
|
7761
|
+
start: xmlNode.getAttribute("start") ? parseInt(xmlNode.getAttribute("start"), 10) : 1,
|
|
7762
|
+
tag: "ol",
|
|
7763
|
+
version: 1
|
|
7764
|
+
});
|
|
7765
|
+
});
|
|
7766
|
+
litexmlService.registerXMLReader("ul", (xmlNode, children) => {
|
|
7767
|
+
return INodeHelper.createElementNode("list", {
|
|
7768
|
+
children,
|
|
7769
|
+
direction: "ltr",
|
|
7770
|
+
format: "",
|
|
7771
|
+
indent: 0,
|
|
7772
|
+
listType: "bullet",
|
|
7773
|
+
start: 1,
|
|
7774
|
+
tag: "ul",
|
|
7775
|
+
version: 1
|
|
7776
|
+
});
|
|
7777
|
+
});
|
|
7778
|
+
litexmlService.registerXMLReader("li", (xmlNode, children) => {
|
|
7779
|
+
return INodeHelper.createElementNode("listitem", {
|
|
5955
7780
|
children,
|
|
5956
7781
|
direction: "ltr",
|
|
5957
7782
|
format: "",
|
|
5958
7783
|
indent: 0,
|
|
5959
|
-
|
|
5960
|
-
type: "link",
|
|
5961
|
-
url: node.url || "",
|
|
7784
|
+
type: "listitem",
|
|
5962
7785
|
version: 1
|
|
5963
|
-
})
|
|
7786
|
+
});
|
|
7787
|
+
});
|
|
7788
|
+
}
|
|
7789
|
+
registerMarkdown() {
|
|
7790
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
7791
|
+
if (!markdownService) return;
|
|
7792
|
+
markdownService.registerMarkdownShortCut({
|
|
7793
|
+
regExp: UNORDERED_LIST_REGEX,
|
|
7794
|
+
replace: listReplace("bullet"),
|
|
7795
|
+
type: "element"
|
|
7796
|
+
});
|
|
7797
|
+
markdownService.registerMarkdownShortCut({
|
|
7798
|
+
regExp: ORDERED_LIST_REGEX,
|
|
7799
|
+
replace: listReplace("number"),
|
|
7800
|
+
type: "element"
|
|
7801
|
+
});
|
|
7802
|
+
markdownService.registerMarkdownShortCut({
|
|
7803
|
+
regExp: CHECK_LIST_REGEX,
|
|
7804
|
+
replace: listReplace("check"),
|
|
7805
|
+
type: "element"
|
|
7806
|
+
});
|
|
7807
|
+
markdownService.registerMarkdownWriter(ListNode.getType(), (ctx, node) => {
|
|
7808
|
+
if ($isListNode(node)) ctx.wrap("", "\n");
|
|
7809
|
+
});
|
|
7810
|
+
const getLevel = (node) => {
|
|
7811
|
+
if (!node) return 0;
|
|
7812
|
+
if ($isRootNode(node.getParent())) return 0;
|
|
7813
|
+
const parent = node.getParent();
|
|
7814
|
+
if (!parent) return 0;
|
|
7815
|
+
return getLevel($getNearestNodeOfType(parent, ListNode)) + 1;
|
|
7816
|
+
};
|
|
7817
|
+
markdownService.registerMarkdownWriter(ListItemNode.getType(), (ctx, node) => {
|
|
7818
|
+
const parent = node.getParent();
|
|
7819
|
+
if ($isListItemNode(node) && $isListNode(parent)) {
|
|
7820
|
+
if ($isListNode(node.getFirstChild())) return;
|
|
7821
|
+
const level = getLevel(parent);
|
|
7822
|
+
const prefix = " ".repeat(level);
|
|
7823
|
+
switch (parent.getListType()) {
|
|
7824
|
+
case "bullet":
|
|
7825
|
+
ctx.wrap(prefix + "- ", "\n");
|
|
7826
|
+
break;
|
|
7827
|
+
case "number":
|
|
7828
|
+
ctx.wrap(`${prefix}${node.getValue()}. `, "\n");
|
|
7829
|
+
break;
|
|
7830
|
+
case "check":
|
|
7831
|
+
ctx.wrap(`${prefix}- [${node.getChecked() ? "x" : " "}] `, "\n");
|
|
7832
|
+
break;
|
|
7833
|
+
default: break;
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
});
|
|
7837
|
+
markdownService.registerMarkdownReader("list", (node, children) => {
|
|
7838
|
+
const isCheck = node.children?.[0]?.type === "listItem" && typeof node.children?.[0]?.checked === "boolean";
|
|
7839
|
+
let start = node.start || 1;
|
|
7840
|
+
return INodeHelper.createElementNode("list", {
|
|
7841
|
+
children: children.map((v) => {
|
|
7842
|
+
if (v.type === "listitem") v.value = start++;
|
|
7843
|
+
return v;
|
|
7844
|
+
}),
|
|
7845
|
+
direction: "ltr",
|
|
7846
|
+
format: "",
|
|
7847
|
+
indent: 0,
|
|
7848
|
+
listType: isCheck ? "check" : node.ordered ? "number" : "bullet",
|
|
7849
|
+
start: node.start || 1,
|
|
7850
|
+
tag: node.ordered ? "ol" : "ul",
|
|
7851
|
+
version: 1
|
|
7852
|
+
});
|
|
7853
|
+
});
|
|
7854
|
+
markdownService.registerMarkdownReader("listItem", (node, children, index) => {
|
|
7855
|
+
return children.map((v) => {
|
|
7856
|
+
if (v.type === "paragraph") return INodeHelper.createElementNode("listitem", {
|
|
7857
|
+
checked: typeof node.checked === "boolean" ? node.checked : void 0,
|
|
7858
|
+
children: v.children,
|
|
7859
|
+
direction: "ltr",
|
|
7860
|
+
format: "",
|
|
7861
|
+
indent: 0,
|
|
7862
|
+
type: "listitem",
|
|
7863
|
+
value: index + 1,
|
|
7864
|
+
version: 1
|
|
7865
|
+
});
|
|
7866
|
+
else if (v.type === "list") return INodeHelper.createElementNode("listitem", {
|
|
7867
|
+
children: [v],
|
|
7868
|
+
direction: "ltr",
|
|
7869
|
+
format: "",
|
|
7870
|
+
indent: 0,
|
|
7871
|
+
type: "listitem",
|
|
7872
|
+
value: index + 1,
|
|
7873
|
+
version: 1
|
|
7874
|
+
});
|
|
7875
|
+
return v;
|
|
7876
|
+
});
|
|
5964
7877
|
});
|
|
5965
7878
|
}
|
|
5966
7879
|
};
|
|
5967
7880
|
//#endregion
|
|
5968
|
-
//#region src/
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
}
|
|
7881
|
+
//#region src/plugins/math/node/index.ts
|
|
7882
|
+
var MathInlineNode = class MathInlineNode extends DecoratorNode {
|
|
7883
|
+
static getType() {
|
|
7884
|
+
return "math";
|
|
7885
|
+
}
|
|
7886
|
+
static clone(node) {
|
|
7887
|
+
return new MathInlineNode(node.__code, node.__key);
|
|
7888
|
+
}
|
|
7889
|
+
static importJSON(serializedNode) {
|
|
7890
|
+
return $createMathInlineNode().updateFromJSON(serializedNode);
|
|
7891
|
+
}
|
|
7892
|
+
static importDOM() {
|
|
7893
|
+
return { span: (node) => {
|
|
7894
|
+
if (node.classList.contains("math-inline")) return {
|
|
7895
|
+
conversion: $convertMathInlineElement,
|
|
7896
|
+
priority: 0
|
|
7897
|
+
};
|
|
7898
|
+
return null;
|
|
7899
|
+
} };
|
|
7900
|
+
}
|
|
7901
|
+
constructor(code = "", key) {
|
|
7902
|
+
super(key);
|
|
7903
|
+
this.__code = code;
|
|
7904
|
+
}
|
|
7905
|
+
get code() {
|
|
7906
|
+
return this.__code;
|
|
7907
|
+
}
|
|
7908
|
+
updateCode(newCode) {
|
|
7909
|
+
const writer = this.getWritable();
|
|
7910
|
+
writer.__code = newCode;
|
|
7911
|
+
}
|
|
7912
|
+
exportDOM() {
|
|
7913
|
+
const span = document.createElement("span");
|
|
7914
|
+
span.className = "math-inline";
|
|
7915
|
+
return { element: span };
|
|
7916
|
+
}
|
|
7917
|
+
createDOM(config) {
|
|
7918
|
+
const element = document.createElement("span");
|
|
7919
|
+
addClassNamesToElement(element, config.theme.mathInline);
|
|
7920
|
+
return element;
|
|
7921
|
+
}
|
|
7922
|
+
exportJSON() {
|
|
7923
|
+
return {
|
|
7924
|
+
...super.exportJSON(),
|
|
7925
|
+
code: this.__code
|
|
7926
|
+
};
|
|
7927
|
+
}
|
|
7928
|
+
updateFromJSON(serializedNode) {
|
|
7929
|
+
const node = super.updateFromJSON(serializedNode);
|
|
7930
|
+
this.__code = serializedNode.code || "";
|
|
7931
|
+
return node;
|
|
7932
|
+
}
|
|
7933
|
+
getTextContent() {
|
|
7934
|
+
return `$${this.code}$`;
|
|
7935
|
+
}
|
|
7936
|
+
isInline() {
|
|
7937
|
+
return true;
|
|
7938
|
+
}
|
|
7939
|
+
updateDOM() {
|
|
7940
|
+
return false;
|
|
7941
|
+
}
|
|
7942
|
+
decorate(editor) {
|
|
7943
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator(MathInlineNode.getType());
|
|
7944
|
+
if (!decorator) return null;
|
|
7945
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
7946
|
+
return {
|
|
7947
|
+
queryDOM: decorator.queryDOM,
|
|
7948
|
+
render: decorator.render(this, editor)
|
|
7949
|
+
};
|
|
7950
|
+
}
|
|
6004
7951
|
};
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
return null;
|
|
6009
|
-
}).length > 0) return true;
|
|
6010
|
-
const anchor = selection.anchor;
|
|
6011
|
-
const focus = selection.focus;
|
|
6012
|
-
const first = focus.isBefore(anchor) ? focus : anchor;
|
|
6013
|
-
const firstBlock = $getNearestBlockElementAncestorOrThrow(first.getNode());
|
|
6014
|
-
if (firstBlock.canIndent()) {
|
|
6015
|
-
const firstBlockKey = firstBlock.getKey();
|
|
6016
|
-
let selectionAtStart = $createRangeSelection();
|
|
6017
|
-
selectionAtStart.anchor.set(firstBlockKey, 0, "element");
|
|
6018
|
-
selectionAtStart.focus.set(firstBlockKey, 0, "element");
|
|
6019
|
-
selectionAtStart = $normalizeSelection__EXPERIMENTAL(selectionAtStart);
|
|
6020
|
-
if (selectionAtStart.anchor.is(first)) return true;
|
|
7952
|
+
var MathBlockNode = class MathBlockNode extends DecoratorNode {
|
|
7953
|
+
static getType() {
|
|
7954
|
+
return "mathBlock";
|
|
6021
7955
|
}
|
|
6022
|
-
|
|
7956
|
+
static clone(node) {
|
|
7957
|
+
return new MathBlockNode(node.__code, node.__key);
|
|
7958
|
+
}
|
|
7959
|
+
static importJSON(serializedNode) {
|
|
7960
|
+
return $createMathBlockNode().updateFromJSON(serializedNode);
|
|
7961
|
+
}
|
|
7962
|
+
static importDOM() {
|
|
7963
|
+
return { div: (node) => {
|
|
7964
|
+
if (node.classList.contains("math-block")) return {
|
|
7965
|
+
conversion: $convertMathBlockElement,
|
|
7966
|
+
priority: 0
|
|
7967
|
+
};
|
|
7968
|
+
return null;
|
|
7969
|
+
} };
|
|
7970
|
+
}
|
|
7971
|
+
constructor(code = "", key) {
|
|
7972
|
+
super(key);
|
|
7973
|
+
this.__code = code;
|
|
7974
|
+
}
|
|
7975
|
+
get code() {
|
|
7976
|
+
return this.__code;
|
|
7977
|
+
}
|
|
7978
|
+
updateCode(newCode) {
|
|
7979
|
+
const writer = this.getWritable();
|
|
7980
|
+
writer.__code = newCode;
|
|
7981
|
+
}
|
|
7982
|
+
exportDOM() {
|
|
7983
|
+
const div = document.createElement("div");
|
|
7984
|
+
div.className = "math-block";
|
|
7985
|
+
return { element: div };
|
|
7986
|
+
}
|
|
7987
|
+
createDOM(config) {
|
|
7988
|
+
const element = document.createElement("div");
|
|
7989
|
+
addClassNamesToElement(element, config.theme.mathBlock);
|
|
7990
|
+
return element;
|
|
7991
|
+
}
|
|
7992
|
+
exportJSON() {
|
|
7993
|
+
return {
|
|
7994
|
+
...super.exportJSON(),
|
|
7995
|
+
code: this.__code
|
|
7996
|
+
};
|
|
7997
|
+
}
|
|
7998
|
+
updateFromJSON(serializedNode) {
|
|
7999
|
+
const node = super.updateFromJSON(serializedNode);
|
|
8000
|
+
this.__code = serializedNode.code || "";
|
|
8001
|
+
return node;
|
|
8002
|
+
}
|
|
8003
|
+
getTextContent() {
|
|
8004
|
+
return `$$\n${this.code}\n$$\n`;
|
|
8005
|
+
}
|
|
8006
|
+
isInline() {
|
|
8007
|
+
return false;
|
|
8008
|
+
}
|
|
8009
|
+
updateDOM() {
|
|
8010
|
+
return false;
|
|
8011
|
+
}
|
|
8012
|
+
decorate(editor) {
|
|
8013
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator(MathBlockNode.getType());
|
|
8014
|
+
if (!decorator) return null;
|
|
8015
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
8016
|
+
return {
|
|
8017
|
+
queryDOM: decorator.queryDOM,
|
|
8018
|
+
render: decorator.render(this, editor)
|
|
8019
|
+
};
|
|
8020
|
+
}
|
|
8021
|
+
};
|
|
8022
|
+
function $createMathInlineNode(code = "") {
|
|
8023
|
+
return $applyNodeReplacement(new MathInlineNode(code));
|
|
8024
|
+
}
|
|
8025
|
+
function $createMathBlockNode(code = "") {
|
|
8026
|
+
return $applyNodeReplacement(new MathBlockNode(code));
|
|
8027
|
+
}
|
|
8028
|
+
function $convertMathInlineElement() {
|
|
8029
|
+
return { node: $createMathInlineNode() };
|
|
8030
|
+
}
|
|
8031
|
+
function $convertMathBlockElement() {
|
|
8032
|
+
return { node: $createMathBlockNode() };
|
|
6023
8033
|
}
|
|
6024
8034
|
//#endregion
|
|
6025
|
-
//#region src/plugins/
|
|
8035
|
+
//#region src/plugins/math/command/index.ts
|
|
8036
|
+
const INSERT_MATH_COMMAND = createCommand("INSERT_MATH_COMMAND");
|
|
8037
|
+
const UPDATE_MATH_COMMAND = createCommand("UPDATE_MATH_COMMAND");
|
|
8038
|
+
const SELECT_MATH_SIDE_COMMAND = createCommand("SELECT_MATH_SIDE_COMMAND");
|
|
8039
|
+
function registerMathCommand(editor) {
|
|
8040
|
+
return mergeRegister(editor.registerCommand(INSERT_MATH_COMMAND, (payload) => {
|
|
8041
|
+
const { code } = payload;
|
|
8042
|
+
editor.update(() => {
|
|
8043
|
+
const mathNode = $createMathInlineNode(code);
|
|
8044
|
+
$insertNodes([mathNode]);
|
|
8045
|
+
if ($isRootOrShadowRoot(mathNode.getParentOrThrow())) $wrapNodeInElement(mathNode, $createParagraphNode).selectEnd();
|
|
8046
|
+
});
|
|
8047
|
+
return true;
|
|
8048
|
+
}, COMMAND_PRIORITY_HIGH), editor.registerCommand(UPDATE_MATH_COMMAND, (payload) => {
|
|
8049
|
+
const { code, key } = payload;
|
|
8050
|
+
editor.update(() => {
|
|
8051
|
+
const mathCode = $getNodeByKey(key);
|
|
8052
|
+
if (mathCode) {
|
|
8053
|
+
mathCode.updateCode(code);
|
|
8054
|
+
mathCode.selectNext();
|
|
8055
|
+
}
|
|
8056
|
+
});
|
|
8057
|
+
return true;
|
|
8058
|
+
}, COMMAND_PRIORITY_HIGH), editor.registerCommand(SELECT_MATH_SIDE_COMMAND, (payload) => {
|
|
8059
|
+
const { key, prev } = payload;
|
|
8060
|
+
editor.update(() => {
|
|
8061
|
+
const mathNode = $getNodeByKey(key);
|
|
8062
|
+
if (mathNode) if (prev) mathNode.selectPrevious();
|
|
8063
|
+
else mathNode.selectNext();
|
|
8064
|
+
});
|
|
8065
|
+
return true;
|
|
8066
|
+
}, COMMAND_PRIORITY_HIGH));
|
|
8067
|
+
}
|
|
8068
|
+
//#endregion
|
|
8069
|
+
//#region src/plugins/math/utils/index.ts
|
|
6026
8070
|
/**
|
|
6027
|
-
*
|
|
8071
|
+
* Heuristic: does the content between `$...$` look like a math formula?
|
|
6028
8072
|
*
|
|
6029
|
-
*
|
|
6030
|
-
*
|
|
8073
|
+
* Positive signals (any one → math):
|
|
8074
|
+
* - LaTeX commands \alpha \frac …
|
|
8075
|
+
* - Structural chars ^ _ { } ( )
|
|
8076
|
+
* - Operators / relations + - * / = < > | ! ,
|
|
8077
|
+
* - Digits 0-9
|
|
8078
|
+
* - Single ASCII letter x y n (common math variable)
|
|
6031
8079
|
*
|
|
8080
|
+
* Everything else (multi-char words, CJK, spaced plain text) → not math.
|
|
6032
8081
|
*/
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
const firstChild = target.firstChild;
|
|
6041
|
-
if (isHTMLElement$1(firstChild) && (firstChild.tagName === "UL" || firstChild.tagName === "OL")) return;
|
|
6042
|
-
const parentNode = target.parentNode;
|
|
6043
|
-
if (!parentNode || parentNode.__lexicalListType !== "check") return;
|
|
6044
|
-
const rect = target.getBoundingClientRect();
|
|
6045
|
-
const zoom = calculateZoomLevel(target);
|
|
6046
|
-
const clientX = event.clientX / zoom;
|
|
6047
|
-
const beforeStyles = window.getComputedStyle ? window.getComputedStyle(target, "::before") : { width: "0px" };
|
|
6048
|
-
const beforeWidthInPixels = parseFloat(beforeStyles.width);
|
|
6049
|
-
const beforeMargin = parseFloat(beforeStyles.marginLeft);
|
|
6050
|
-
const clickAreaPadding = event.pointerType === "touch" ? 32 : 0;
|
|
6051
|
-
if (target.dir === "rtl" ? clientX < rect.right + clickAreaPadding && clientX > rect.right - beforeWidthInPixels - clickAreaPadding : clientX > rect.left + beforeMargin - clickAreaPadding && clientX < rect.left + beforeMargin + beforeWidthInPixels + clickAreaPadding) callback();
|
|
6052
|
-
}
|
|
6053
|
-
function handleClick(event) {
|
|
6054
|
-
handleCheckItemEvent(event, () => {
|
|
6055
|
-
if (isHTMLElement$1(event.target)) {
|
|
6056
|
-
const domNode = event.target;
|
|
6057
|
-
const editor = getNearestEditorFromDOMNode(domNode);
|
|
6058
|
-
if (editor && editor.isEditable()) editor.update(() => {
|
|
6059
|
-
const node = $getNearestNodeFromDOMNode(domNode);
|
|
6060
|
-
if ($isListItemNode(node)) {
|
|
6061
|
-
domNode.focus();
|
|
6062
|
-
node.toggleChecked();
|
|
6063
|
-
}
|
|
6064
|
-
});
|
|
6065
|
-
}
|
|
6066
|
-
});
|
|
6067
|
-
}
|
|
6068
|
-
function handlePointerDown(event) {
|
|
6069
|
-
handleCheckItemEvent(event, () => {
|
|
6070
|
-
event.preventDefault();
|
|
6071
|
-
});
|
|
6072
|
-
}
|
|
6073
|
-
function getActiveCheckListItem() {
|
|
6074
|
-
const activeElement = document.activeElement;
|
|
6075
|
-
return isHTMLElement$1(activeElement) && activeElement.tagName === "LI" && activeElement.parentNode && activeElement.parentNode.__lexicalListType === "check" ? activeElement : null;
|
|
8082
|
+
function isLikelyMathContent(content) {
|
|
8083
|
+
const trimmed = content.trim();
|
|
8084
|
+
if (!trimmed) return false;
|
|
8085
|
+
if (/\\[A-Za-z]/.test(trimmed)) return true;
|
|
8086
|
+
if (/[\d!()*+,/<=>^_{|}-]/.test(trimmed)) return true;
|
|
8087
|
+
if (/^[A-Za-z]$/.test(trimmed)) return true;
|
|
8088
|
+
return false;
|
|
6076
8089
|
}
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
if (parent) sibling = backward ? parent.getPreviousSibling() : parent.getNextSibling();
|
|
8090
|
+
//#endregion
|
|
8091
|
+
//#region src/plugins/math/plugin/index.ts
|
|
8092
|
+
const MathPlugin = class extends KernelPlugin {
|
|
8093
|
+
static {
|
|
8094
|
+
this.pluginName = "MathPlugin";
|
|
6083
8095
|
}
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
8096
|
+
constructor(kernel, config) {
|
|
8097
|
+
super();
|
|
8098
|
+
this.kernel = kernel;
|
|
8099
|
+
this.config = config;
|
|
8100
|
+
this.logger = createDebugLogger("plugin", "math");
|
|
8101
|
+
kernel.registerNodes([MathInlineNode, MathBlockNode]);
|
|
8102
|
+
if (config?.theme) kernel.registerThemes(config?.theme);
|
|
8103
|
+
this.registerDecorator(kernel, MathInlineNode.getType(), (node, editor) => {
|
|
8104
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
8105
|
+
});
|
|
8106
|
+
this.registerDecorator(kernel, MathBlockNode.getType(), (node, editor) => {
|
|
8107
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
8108
|
+
});
|
|
6088
8109
|
}
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
event.preventDefault();
|
|
6102
|
-
setTimeout(() => {
|
|
6103
|
-
dom.focus();
|
|
6104
|
-
}, 0);
|
|
8110
|
+
onInit(editor) {
|
|
8111
|
+
this.register(registerMathCommand(editor));
|
|
8112
|
+
this.registerMarkdown();
|
|
8113
|
+
this.registerLiteXml();
|
|
8114
|
+
}
|
|
8115
|
+
registerLiteXml() {
|
|
8116
|
+
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
8117
|
+
if (!litexmlService) return;
|
|
8118
|
+
litexmlService.registerXMLWriter(MathInlineNode.getType(), (node, ctx) => {
|
|
8119
|
+
if (node instanceof MathInlineNode) {
|
|
8120
|
+
const attributes = { code: node.getTextContent() };
|
|
8121
|
+
return ctx.createXmlNode("math", attributes);
|
|
6105
8122
|
}
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
8123
|
+
return false;
|
|
8124
|
+
});
|
|
8125
|
+
litexmlService.registerXMLWriter(MathBlockNode.getType(), (node, ctx) => {
|
|
8126
|
+
if (node instanceof MathBlockNode) {
|
|
8127
|
+
const attributes = { code: node.getTextContent() };
|
|
8128
|
+
return ctx.createXmlNode("mathBlock", attributes);
|
|
8129
|
+
}
|
|
8130
|
+
return false;
|
|
8131
|
+
});
|
|
8132
|
+
litexmlService.registerXMLReader("math", (xmlNode) => {
|
|
8133
|
+
return INodeHelper.createElementNode(MathInlineNode.getType(), {
|
|
8134
|
+
code: xmlNode.getAttribute("code") || "",
|
|
8135
|
+
version: 1
|
|
8136
|
+
});
|
|
8137
|
+
});
|
|
8138
|
+
litexmlService.registerXMLReader("mathBlock", (xmlNode) => {
|
|
8139
|
+
return INodeHelper.createElementNode(MathBlockNode.getType(), {
|
|
8140
|
+
code: xmlNode.getAttribute("code") || "",
|
|
8141
|
+
version: 1
|
|
8142
|
+
});
|
|
8143
|
+
});
|
|
8144
|
+
}
|
|
8145
|
+
registerMarkdown() {
|
|
8146
|
+
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
8147
|
+
markdownService?.registerMarkdownShortCut({
|
|
8148
|
+
regExp: /\$([^\s$](?:[^$]*[^\s$])?)\$\s?$/,
|
|
8149
|
+
replace: (textNode, match) => {
|
|
8150
|
+
const [, code] = match;
|
|
8151
|
+
if (!isLikelyMathContent(code)) return;
|
|
8152
|
+
const mathNode = $createMathInlineNode(code);
|
|
8153
|
+
this.logger.debug("Math node inserted:", mathNode);
|
|
8154
|
+
textNode.insertBefore(mathNode);
|
|
8155
|
+
textNode.setTextContent("");
|
|
8156
|
+
textNode.select();
|
|
8157
|
+
},
|
|
8158
|
+
trigger: "$",
|
|
8159
|
+
type: "text-match"
|
|
8160
|
+
});
|
|
8161
|
+
markdownService?.registerMarkdownShortCut({
|
|
8162
|
+
regExp: /^(\$\$)$/,
|
|
8163
|
+
replace: (parentNode, _1, _2, isImport) => {
|
|
8164
|
+
const node = $createMathBlockNode();
|
|
8165
|
+
if (isImport || parentNode.getNextSibling()) parentNode.replace(node);
|
|
8166
|
+
else parentNode.insertBefore(node);
|
|
8167
|
+
const sel = $createNodeSelection();
|
|
8168
|
+
sel.add(node.getKey());
|
|
8169
|
+
$setSelection(sel);
|
|
8170
|
+
},
|
|
8171
|
+
trigger: "enter",
|
|
8172
|
+
type: "element"
|
|
8173
|
+
});
|
|
8174
|
+
markdownService?.registerMarkdownWriter(MathInlineNode.getType(), (ctx, node) => {
|
|
8175
|
+
ctx.appendLine(node.getTextContent());
|
|
6114
8176
|
return true;
|
|
6115
|
-
}
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
const selection = $getSelection();
|
|
6125
|
-
if ($isRangeSelection(selection) && selection.isCollapsed()) {
|
|
6126
|
-
const { anchor } = selection;
|
|
6127
|
-
const isElement = anchor.type === "element";
|
|
6128
|
-
if (isElement || anchor.offset === 0) {
|
|
6129
|
-
const anchorNode = anchor.getNode();
|
|
6130
|
-
const elementNode = $findMatchingParent(anchorNode, (node) => $isElementNode(node) && !node.isInline());
|
|
6131
|
-
if ($isListItemNode(elementNode)) {
|
|
6132
|
-
const parent = elementNode.getParent();
|
|
6133
|
-
if ($isListNode(parent) && parent.getListType() === "check" && (isElement || elementNode.getFirstDescendant() === anchorNode)) {
|
|
6134
|
-
const domNode = editor.getElementByKey(elementNode.__key);
|
|
6135
|
-
if (domNode && document.activeElement !== domNode) {
|
|
6136
|
-
domNode.focus();
|
|
6137
|
-
event.preventDefault();
|
|
6138
|
-
return true;
|
|
6139
|
-
}
|
|
6140
|
-
}
|
|
6141
|
-
}
|
|
6142
|
-
}
|
|
6143
|
-
}
|
|
6144
|
-
return false;
|
|
8177
|
+
});
|
|
8178
|
+
markdownService?.registerMarkdownWriter(MathBlockNode.getType(), (ctx, node) => {
|
|
8179
|
+
ctx.appendLine(node.getTextContent());
|
|
8180
|
+
return true;
|
|
8181
|
+
});
|
|
8182
|
+
markdownService?.registerMarkdownReader("inlineMath", (node) => {
|
|
8183
|
+
return INodeHelper.createElementNode("math", {
|
|
8184
|
+
code: node.value,
|
|
8185
|
+
version: 1
|
|
6145
8186
|
});
|
|
6146
|
-
}
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
rootElement.addEventListener("pointerdown", handlePointerDown);
|
|
6152
|
-
}
|
|
6153
|
-
if (prevElement !== null) {
|
|
6154
|
-
prevElement.removeEventListener("click", handleClick);
|
|
6155
|
-
prevElement.removeEventListener("pointerdown", handlePointerDown);
|
|
6156
|
-
}
|
|
6157
|
-
}));
|
|
6158
|
-
return mergeRegister(...registrations);
|
|
6159
|
-
}
|
|
6160
|
-
//#endregion
|
|
6161
|
-
//#region src/plugins/list/plugin/registry.ts
|
|
6162
|
-
function registerListCommands(editor, kernel, options) {
|
|
6163
|
-
const { enableHotkey = true } = options || {};
|
|
6164
|
-
return mergeRegister(kernel.registerHotkey(HotkeyEnum.BulletList, () => editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, void 0), {
|
|
6165
|
-
enabled: enableHotkey,
|
|
6166
|
-
preventDefault: true,
|
|
6167
|
-
stopImmediatePropagation: true
|
|
6168
|
-
}), kernel.registerHotkey(HotkeyEnum.NumberList, () => editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, void 0), {
|
|
6169
|
-
enabled: enableHotkey,
|
|
6170
|
-
preventDefault: true,
|
|
6171
|
-
stopImmediatePropagation: true
|
|
6172
|
-
}), editor.registerCommand(KEY_TAB_COMMAND, (event) => {
|
|
6173
|
-
const selection = $getSelection();
|
|
6174
|
-
if (!$isRangeSelection(selection)) return false;
|
|
6175
|
-
event.preventDefault();
|
|
6176
|
-
const command = $indentOverTab(selection) ? event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND : INSERT_TAB_COMMAND;
|
|
6177
|
-
return editor.dispatchCommand(command, void 0);
|
|
6178
|
-
}, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_BACKSPACE_COMMAND, (event) => {
|
|
6179
|
-
const selection = $getSelection();
|
|
6180
|
-
if (!$isRangeSelection(selection) || !selection.isCollapsed()) return false;
|
|
6181
|
-
const anchor = selection.anchor;
|
|
6182
|
-
if (anchor.offset !== 0) return false;
|
|
6183
|
-
const anchorNode = anchor.getNode();
|
|
6184
|
-
let listItemNode;
|
|
6185
|
-
if ($isListItemNode(anchorNode)) listItemNode = anchorNode;
|
|
6186
|
-
else if ($isTextNode(anchorNode)) {
|
|
6187
|
-
if (anchorNode.getPreviousSibling()) return false;
|
|
6188
|
-
const parent = anchorNode.getParentOrThrow();
|
|
6189
|
-
if (!$isListItemNode(parent)) return false;
|
|
6190
|
-
listItemNode = parent;
|
|
6191
|
-
}
|
|
6192
|
-
if (!listItemNode || !$isRootNode(listItemNode.getParent()?.getParent())) return false;
|
|
6193
|
-
const listNode = listItemNode.getParentOrThrow();
|
|
6194
|
-
queueMicrotask(() => {
|
|
6195
|
-
editor.update(() => {
|
|
6196
|
-
if (!listItemNode) return;
|
|
6197
|
-
let newlistNode;
|
|
6198
|
-
if (listItemNode.getPreviousSibling() === null) {
|
|
6199
|
-
listItemNode.replace($createParagraphNode(), true).select(0, 0);
|
|
6200
|
-
return;
|
|
6201
|
-
}
|
|
6202
|
-
let next = listItemNode.getNextSibling();
|
|
6203
|
-
if (next) newlistNode = $createListNode(listNode.getListType(), listItemNode.getValue());
|
|
6204
|
-
while (next && newlistNode) {
|
|
6205
|
-
next.remove();
|
|
6206
|
-
newlistNode.append(next);
|
|
6207
|
-
next = next.getNextSibling();
|
|
6208
|
-
}
|
|
6209
|
-
const p = listItemNode.replace($createParagraphNode(), true);
|
|
6210
|
-
p.remove();
|
|
6211
|
-
listNode.insertAfter(p);
|
|
6212
|
-
if (newlistNode) p.insertAfter(newlistNode);
|
|
6213
|
-
p.select(0, 0);
|
|
8187
|
+
});
|
|
8188
|
+
markdownService?.registerMarkdownReader("math", (node) => {
|
|
8189
|
+
return INodeHelper.createElementNode("mathBlock", {
|
|
8190
|
+
code: node.value,
|
|
8191
|
+
version: 1
|
|
6214
8192
|
});
|
|
6215
8193
|
});
|
|
6216
|
-
|
|
6217
|
-
|
|
8194
|
+
}
|
|
8195
|
+
};
|
|
8196
|
+
//#endregion
|
|
8197
|
+
//#region src/plugins/mention/node/MentionNode.ts
|
|
8198
|
+
var MentionNode = class MentionNode extends DecoratorNode {
|
|
8199
|
+
static getType() {
|
|
8200
|
+
return "mention";
|
|
8201
|
+
}
|
|
8202
|
+
static clone(node) {
|
|
8203
|
+
return new MentionNode(node.__label, node.__metadata, node.__key);
|
|
8204
|
+
}
|
|
8205
|
+
static importJSON(serializedNode) {
|
|
8206
|
+
return $createMentionNode().updateFromJSON(serializedNode);
|
|
8207
|
+
}
|
|
8208
|
+
static importDOM() {
|
|
8209
|
+
return { span: (node) => {
|
|
8210
|
+
if (node.classList.contains("mention")) return {
|
|
8211
|
+
conversion: $convertMentionElement,
|
|
8212
|
+
priority: 0
|
|
8213
|
+
};
|
|
8214
|
+
return null;
|
|
8215
|
+
} };
|
|
8216
|
+
}
|
|
8217
|
+
constructor(label = "", metadata = {}, key) {
|
|
8218
|
+
super(key);
|
|
8219
|
+
this.__label = label;
|
|
8220
|
+
this.__metadata = metadata;
|
|
8221
|
+
}
|
|
8222
|
+
get label() {
|
|
8223
|
+
return this.__label;
|
|
8224
|
+
}
|
|
8225
|
+
get metadata() {
|
|
8226
|
+
return this.__metadata;
|
|
8227
|
+
}
|
|
8228
|
+
exportDOM() {
|
|
8229
|
+
return { element: document.createElement("span") };
|
|
8230
|
+
}
|
|
8231
|
+
createDOM(config) {
|
|
8232
|
+
const element = document.createElement("span");
|
|
8233
|
+
addClassNamesToElement(element, config.theme.mention);
|
|
8234
|
+
return element;
|
|
8235
|
+
}
|
|
8236
|
+
getTextContent() {
|
|
8237
|
+
return this.label;
|
|
8238
|
+
}
|
|
8239
|
+
isInline() {
|
|
8240
|
+
return true;
|
|
8241
|
+
}
|
|
8242
|
+
updateDOM() {
|
|
8243
|
+
return false;
|
|
8244
|
+
}
|
|
8245
|
+
exportJSON() {
|
|
8246
|
+
return {
|
|
8247
|
+
...super.exportJSON(),
|
|
8248
|
+
label: this.label,
|
|
8249
|
+
metadata: this.metadata
|
|
8250
|
+
};
|
|
8251
|
+
}
|
|
8252
|
+
updateFromJSON(serializedNode) {
|
|
8253
|
+
const node = super.updateFromJSON(serializedNode);
|
|
8254
|
+
this.__label = serializedNode.label || "";
|
|
8255
|
+
this.__metadata = serializedNode.metadata || {};
|
|
8256
|
+
return node;
|
|
8257
|
+
}
|
|
8258
|
+
decorate(editor) {
|
|
8259
|
+
const decorator = getKernelFromEditor(editor)?.getDecorator(MentionNode.getType());
|
|
8260
|
+
if (!decorator) return null;
|
|
8261
|
+
if (typeof decorator === "function") return decorator(this, editor);
|
|
8262
|
+
return {
|
|
8263
|
+
queryDOM: decorator.queryDOM,
|
|
8264
|
+
render: decorator.render(this, editor)
|
|
8265
|
+
};
|
|
8266
|
+
}
|
|
8267
|
+
};
|
|
8268
|
+
function $createMentionNode(label, metadata) {
|
|
8269
|
+
return $applyNodeReplacement(new MentionNode(label, metadata));
|
|
8270
|
+
}
|
|
8271
|
+
function $convertMentionElement() {
|
|
8272
|
+
return { node: $createMentionNode() };
|
|
8273
|
+
}
|
|
8274
|
+
function $isMentionNode(node) {
|
|
8275
|
+
return node.getType() === MentionNode.getType();
|
|
8276
|
+
}
|
|
8277
|
+
//#endregion
|
|
8278
|
+
//#region src/plugins/mention/command/index.ts
|
|
8279
|
+
const INSERT_MENTION_COMMAND = createCommand("INSERT_MENTION_COMMAND");
|
|
8280
|
+
function registerMentionCommand(editor) {
|
|
8281
|
+
return editor.registerCommand(INSERT_MENTION_COMMAND, (payload) => {
|
|
8282
|
+
const { metadata, label } = payload;
|
|
8283
|
+
editor.update(() => {
|
|
8284
|
+
const mentionNode = $createMentionNode(label, metadata);
|
|
8285
|
+
$insertNodes([mentionNode]);
|
|
8286
|
+
if ($isRootOrShadowRoot(mentionNode.getParentOrThrow())) $wrapNodeInElement(mentionNode, $createParagraphNode).selectEnd();
|
|
8287
|
+
});
|
|
6218
8288
|
return true;
|
|
6219
|
-
},
|
|
8289
|
+
}, COMMAND_PRIORITY_HIGH);
|
|
6220
8290
|
}
|
|
6221
8291
|
//#endregion
|
|
6222
|
-
//#region src/plugins/
|
|
6223
|
-
|
|
6224
|
-
const
|
|
6225
|
-
|
|
6226
|
-
const
|
|
8292
|
+
//#region src/plugins/mention/plugin/register.ts
|
|
8293
|
+
function registerMentionNodeSelectionObserver(editor) {
|
|
8294
|
+
const selectMentionKeys = [];
|
|
8295
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
8296
|
+
const selection = editorState.read(() => $getSelection());
|
|
8297
|
+
const newSelectMentionKeys = [];
|
|
8298
|
+
if ($isNodeSelection(selection)) editorState.read(() => selection.getNodes()).forEach((node) => {
|
|
8299
|
+
if (node.getType() === "mention") newSelectMentionKeys.push(node.getKey());
|
|
8300
|
+
});
|
|
8301
|
+
else if ($isRangeSelection(selection) && !selection.isCollapsed()) editorState.read(() => {
|
|
8302
|
+
selection.getNodes().forEach((node) => {
|
|
8303
|
+
if ($isMentionNode(node)) newSelectMentionKeys.push(node.getKey());
|
|
8304
|
+
});
|
|
8305
|
+
});
|
|
8306
|
+
const removeKeys = selectMentionKeys.filter((key) => !newSelectMentionKeys.includes(key));
|
|
8307
|
+
const addKeys = newSelectMentionKeys.filter((key) => !selectMentionKeys.includes(key));
|
|
8308
|
+
selectMentionKeys.length = 0;
|
|
8309
|
+
selectMentionKeys.push(...newSelectMentionKeys);
|
|
8310
|
+
removeKeys.forEach((key) => {
|
|
8311
|
+
editor.getElementByKey(key)?.classList.remove("selected");
|
|
8312
|
+
});
|
|
8313
|
+
addKeys.forEach((key) => {
|
|
8314
|
+
editor.getElementByKey(key)?.classList.add("selected");
|
|
8315
|
+
});
|
|
8316
|
+
});
|
|
8317
|
+
}
|
|
8318
|
+
//#endregion
|
|
8319
|
+
//#region src/plugins/mention/plugin/index.ts
|
|
8320
|
+
const MentionPlugin = class extends KernelPlugin {
|
|
6227
8321
|
static {
|
|
6228
|
-
this.pluginName = "
|
|
8322
|
+
this.pluginName = "MentionPlugin";
|
|
6229
8323
|
}
|
|
6230
8324
|
constructor(kernel, config) {
|
|
6231
8325
|
super();
|
|
6232
8326
|
this.kernel = kernel;
|
|
6233
8327
|
this.config = config;
|
|
6234
|
-
kernel.registerNodes([
|
|
6235
|
-
kernel.registerThemes(
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
listitemUnchecked: "editor_listItemUnchecked",
|
|
6240
|
-
nested: { listitem: "editor_listItemNested" },
|
|
6241
|
-
ol: cx("editor_listOrdered", config?.theme),
|
|
6242
|
-
olDepth: [
|
|
6243
|
-
"editor_listOrdered dp1",
|
|
6244
|
-
"editor_listOrdered dp2",
|
|
6245
|
-
"editor_listOrdered dp3",
|
|
6246
|
-
"editor_listOrdered dp4",
|
|
6247
|
-
"editor_listOrdered dp5"
|
|
6248
|
-
],
|
|
6249
|
-
ul: cx("editor_listUnordered", config?.theme)
|
|
6250
|
-
} });
|
|
8328
|
+
kernel.registerNodes([MentionNode]);
|
|
8329
|
+
if (config?.theme) kernel.registerThemes(config?.theme);
|
|
8330
|
+
this.registerDecorator(kernel, MentionNode.getType(), (node, editor) => {
|
|
8331
|
+
return config?.decorator ? config.decorator(node, editor) : null;
|
|
8332
|
+
});
|
|
6251
8333
|
}
|
|
6252
8334
|
onInit(editor) {
|
|
6253
|
-
this.register(
|
|
6254
|
-
this.register(
|
|
6255
|
-
this.register(registerListStrictIndentTransform(editor));
|
|
6256
|
-
this.register(registerListCommands(editor, this.kernel, { enableHotkey: this.config?.enableHotkey }));
|
|
8335
|
+
this.register(registerMentionCommand(editor));
|
|
8336
|
+
if (this.config?.decorator) this.register(registerMentionNodeSelectionObserver(editor));
|
|
6257
8337
|
this.registerMarkdown();
|
|
6258
8338
|
this.registerLiteXml();
|
|
6259
8339
|
}
|
|
6260
8340
|
registerLiteXml() {
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
const attributes = {};
|
|
6267
|
-
if (node.getListType() === "number" && node.getStart() !== 1) attributes.start = node.getStart().toString();
|
|
6268
|
-
return ctx.createXmlNode(tagName, attributes);
|
|
8341
|
+
this.kernel.requireService(ILitexmlService)?.registerXMLWriter(MentionNode.getType(), (node, ctx) => {
|
|
8342
|
+
if ($isMentionNode(node)) {
|
|
8343
|
+
const attributes = { label: node.label };
|
|
8344
|
+
if (node.metadata && Object.keys(node.metadata).length > 0) attributes.metadata = JSON.stringify(node.metadata);
|
|
8345
|
+
return ctx.createXmlNode("mention", attributes);
|
|
6269
8346
|
}
|
|
6270
8347
|
return false;
|
|
6271
8348
|
});
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
litexmlService.registerXMLReader("ol", (xmlNode, children) => {
|
|
6277
|
-
return INodeHelper.createElementNode("list", {
|
|
6278
|
-
children,
|
|
6279
|
-
direction: "ltr",
|
|
6280
|
-
format: "",
|
|
6281
|
-
indent: 0,
|
|
6282
|
-
listType: "number",
|
|
6283
|
-
start: xmlNode.getAttribute("start") ? parseInt(xmlNode.getAttribute("start"), 10) : 1,
|
|
6284
|
-
tag: "ol",
|
|
6285
|
-
version: 1
|
|
6286
|
-
});
|
|
6287
|
-
});
|
|
6288
|
-
litexmlService.registerXMLReader("ul", (xmlNode, children) => {
|
|
6289
|
-
return INodeHelper.createElementNode("list", {
|
|
6290
|
-
children,
|
|
6291
|
-
direction: "ltr",
|
|
6292
|
-
format: "",
|
|
6293
|
-
indent: 0,
|
|
6294
|
-
listType: "bullet",
|
|
6295
|
-
start: 1,
|
|
6296
|
-
tag: "ul",
|
|
6297
|
-
version: 1
|
|
6298
|
-
});
|
|
6299
|
-
});
|
|
6300
|
-
litexmlService.registerXMLReader("li", (xmlNode, children) => {
|
|
6301
|
-
return INodeHelper.createElementNode("listitem", {
|
|
6302
|
-
children,
|
|
6303
|
-
direction: "ltr",
|
|
6304
|
-
format: "",
|
|
6305
|
-
indent: 0,
|
|
6306
|
-
type: "listitem",
|
|
6307
|
-
version: 1
|
|
8349
|
+
this.kernel.requireService(ILitexmlService)?.registerXMLReader("mention", (xmlNode) => {
|
|
8350
|
+
return INodeHelper.createElementNode(MentionNode.getType(), {
|
|
8351
|
+
label: xmlNode.getAttribute("label") || "",
|
|
8352
|
+
metadata: xmlNode.getAttribute("metadata") ? JSON.parse(xmlNode.getAttribute("metadata")) : {}
|
|
6308
8353
|
});
|
|
6309
8354
|
});
|
|
6310
8355
|
}
|
|
6311
8356
|
registerMarkdown() {
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
type: "element"
|
|
6318
|
-
});
|
|
6319
|
-
markdownService.registerMarkdownShortCut({
|
|
6320
|
-
regExp: ORDERED_LIST_REGEX,
|
|
6321
|
-
replace: listReplace("number"),
|
|
6322
|
-
type: "element"
|
|
6323
|
-
});
|
|
6324
|
-
markdownService.registerMarkdownShortCut({
|
|
6325
|
-
regExp: CHECK_LIST_REGEX,
|
|
6326
|
-
replace: listReplace("check"),
|
|
6327
|
-
type: "element"
|
|
6328
|
-
});
|
|
6329
|
-
markdownService.registerMarkdownWriter(ListNode.getType(), (ctx, node) => {
|
|
6330
|
-
if ($isListNode(node)) ctx.wrap("", "\n");
|
|
6331
|
-
});
|
|
6332
|
-
const getLevel = (node) => {
|
|
6333
|
-
if (!node) return 0;
|
|
6334
|
-
if ($isRootNode(node.getParent())) return 0;
|
|
6335
|
-
const parent = node.getParent();
|
|
6336
|
-
if (!parent) return 0;
|
|
6337
|
-
return getLevel($getNearestNodeOfType(parent, ListNode)) + 1;
|
|
6338
|
-
};
|
|
6339
|
-
markdownService.registerMarkdownWriter(ListItemNode.getType(), (ctx, node) => {
|
|
6340
|
-
const parent = node.getParent();
|
|
6341
|
-
if ($isListItemNode(node) && $isListNode(parent)) {
|
|
6342
|
-
if ($isListNode(node.getFirstChild())) return;
|
|
6343
|
-
const level = getLevel(parent);
|
|
6344
|
-
const prefix = " ".repeat(level);
|
|
6345
|
-
switch (parent.getListType()) {
|
|
6346
|
-
case "bullet":
|
|
6347
|
-
ctx.wrap(prefix + "- ", "\n");
|
|
6348
|
-
break;
|
|
6349
|
-
case "number":
|
|
6350
|
-
ctx.wrap(`${prefix}${node.getValue()}. `, "\n");
|
|
6351
|
-
break;
|
|
6352
|
-
case "check":
|
|
6353
|
-
ctx.wrap(`${prefix}- [${node.getChecked() ? "x" : " "}] `, "\n");
|
|
6354
|
-
break;
|
|
6355
|
-
default: break;
|
|
8357
|
+
this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownWriter(MentionNode.getType(), (ctx, node) => {
|
|
8358
|
+
if ($isMentionNode(node)) {
|
|
8359
|
+
if (this.config?.markdownWriter) {
|
|
8360
|
+
ctx.appendLine(this.config.markdownWriter(node));
|
|
8361
|
+
return;
|
|
6356
8362
|
}
|
|
8363
|
+
ctx.appendLine(`${node.label}`);
|
|
6357
8364
|
}
|
|
6358
8365
|
});
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
return INodeHelper.createElementNode("list", {
|
|
6363
|
-
children: children.map((v) => {
|
|
6364
|
-
if (v.type === "listitem") v.value = start++;
|
|
6365
|
-
return v;
|
|
6366
|
-
}),
|
|
6367
|
-
direction: "ltr",
|
|
6368
|
-
format: "",
|
|
6369
|
-
indent: 0,
|
|
6370
|
-
listType: isCheck ? "check" : node.ordered ? "number" : "bullet",
|
|
6371
|
-
start: node.start || 1,
|
|
6372
|
-
tag: node.ordered ? "ol" : "ul",
|
|
6373
|
-
version: 1
|
|
6374
|
-
});
|
|
6375
|
-
});
|
|
6376
|
-
markdownService.registerMarkdownReader("listItem", (node, children, index) => {
|
|
6377
|
-
return children.map((v) => {
|
|
6378
|
-
if (v.type === "paragraph") return INodeHelper.createElementNode("listitem", {
|
|
6379
|
-
checked: typeof node.checked === "boolean" ? node.checked : void 0,
|
|
6380
|
-
children: v.children,
|
|
6381
|
-
direction: "ltr",
|
|
6382
|
-
format: "",
|
|
6383
|
-
indent: 0,
|
|
6384
|
-
type: "listitem",
|
|
6385
|
-
value: index + 1,
|
|
6386
|
-
version: 1
|
|
6387
|
-
});
|
|
6388
|
-
else if (v.type === "list") return INodeHelper.createElementNode("listitem", {
|
|
6389
|
-
children: [v],
|
|
6390
|
-
direction: "ltr",
|
|
6391
|
-
format: "",
|
|
6392
|
-
indent: 0,
|
|
6393
|
-
type: "listitem",
|
|
6394
|
-
value: index + 1,
|
|
6395
|
-
version: 1
|
|
6396
|
-
});
|
|
6397
|
-
return v;
|
|
6398
|
-
});
|
|
6399
|
-
});
|
|
8366
|
+
if (this.config?.markdownReader) this.kernel.requireService(IMarkdownShortCutService)?.registerMarkdownReader("html", (node, children) => {
|
|
8367
|
+
return this.config?.markdownReader ? this.config.markdownReader(node, children) || false : false;
|
|
8368
|
+
}, 1);
|
|
6400
8369
|
}
|
|
6401
8370
|
};
|
|
6402
8371
|
//#endregion
|
|
@@ -7286,85 +9255,6 @@ const TablePlugin = class extends KernelPlugin {
|
|
|
7286
9255
|
}
|
|
7287
9256
|
};
|
|
7288
9257
|
//#endregion
|
|
7289
|
-
//#region src/plugins/codeblock/utils/language.ts
|
|
7290
|
-
function getCodeLanguageByInput(input) {
|
|
7291
|
-
if (!input) return "plaintext";
|
|
7292
|
-
const inputLang = input.toLocaleLowerCase();
|
|
7293
|
-
return bundledLanguagesInfo.find((lang) => lang.id === inputLang || lang.aliases?.includes(inputLang))?.id || "plaintext";
|
|
7294
|
-
}
|
|
7295
|
-
//#endregion
|
|
7296
|
-
//#region src/headless/plugins/codeblock.ts
|
|
7297
|
-
const createCodeChildren = (code) => code.split("\n").flatMap((text, index, array) => {
|
|
7298
|
-
const textNode = INodeHelper.createTextNode(text);
|
|
7299
|
-
textNode.type = "code-highlight";
|
|
7300
|
-
if (index === array.length - 1) return textNode;
|
|
7301
|
-
return [textNode, {
|
|
7302
|
-
type: "linebreak",
|
|
7303
|
-
version: 1
|
|
7304
|
-
}];
|
|
7305
|
-
}).flat();
|
|
7306
|
-
const HeadlessCodeblockPlugin = class extends KernelPlugin {
|
|
7307
|
-
static {
|
|
7308
|
-
this.pluginName = "HeadlessCodeblockPlugin";
|
|
7309
|
-
}
|
|
7310
|
-
constructor(kernel) {
|
|
7311
|
-
super();
|
|
7312
|
-
this.kernel = kernel;
|
|
7313
|
-
kernel.registerNodes([CodeNode, CodeHighlightNode]);
|
|
7314
|
-
kernel.registerThemes({ code: "editor-code" });
|
|
7315
|
-
}
|
|
7316
|
-
onInit() {
|
|
7317
|
-
this.registerMarkdown();
|
|
7318
|
-
this.registerLiteXml();
|
|
7319
|
-
}
|
|
7320
|
-
registerLiteXml() {
|
|
7321
|
-
const litexmlService = this.kernel.requireService(ILitexmlService);
|
|
7322
|
-
if (!litexmlService) return;
|
|
7323
|
-
litexmlService.registerXMLWriter(CodeNode.getType(), (node, ctx) => {
|
|
7324
|
-
const codeNode = node;
|
|
7325
|
-
return ctx.createXmlNode("code", { lang: codeNode.getLanguage() || "plaintext" }, codeNode.getTextContent());
|
|
7326
|
-
});
|
|
7327
|
-
litexmlService.registerXMLReader("code", (xmlElement, children) => {
|
|
7328
|
-
const text = children.map((value) => value.text || "").join("");
|
|
7329
|
-
return INodeHelper.createElementNode(CodeNode.getType(), {
|
|
7330
|
-
children: createCodeChildren(text),
|
|
7331
|
-
direction: "ltr",
|
|
7332
|
-
format: "",
|
|
7333
|
-
indent: 0,
|
|
7334
|
-
language: xmlElement.getAttribute("lang"),
|
|
7335
|
-
version: 1
|
|
7336
|
-
});
|
|
7337
|
-
});
|
|
7338
|
-
}
|
|
7339
|
-
registerMarkdown() {
|
|
7340
|
-
const markdownService = this.kernel.requireService(IMarkdownShortCutService);
|
|
7341
|
-
if (!markdownService) return;
|
|
7342
|
-
markdownService.registerMarkdownWriter(CodeNode.getType(), (ctx, node) => {
|
|
7343
|
-
if ($isCodeNode(node)) ctx.wrap("```" + (node.getLanguage() || "") + "\n", "\n```\n");
|
|
7344
|
-
});
|
|
7345
|
-
markdownService.registerMarkdownWriter(TabNode.getType(), (ctx) => {
|
|
7346
|
-
ctx.appendLine(" ");
|
|
7347
|
-
});
|
|
7348
|
-
markdownService.registerMarkdownWriter(CodeHighlightNode.getType(), (ctx, node) => {
|
|
7349
|
-
if ($isCodeHighlightNode(node)) ctx.appendLine(node.getTextContent());
|
|
7350
|
-
});
|
|
7351
|
-
markdownService.registerMarkdownWriter("linebreak", (ctx, node) => {
|
|
7352
|
-
if ($isCodeNode(node.getParent())) ctx.appendLine("\n");
|
|
7353
|
-
});
|
|
7354
|
-
markdownService.registerMarkdownReader("code", (node) => {
|
|
7355
|
-
const language = node.lang ? getCodeLanguageByInput(node.lang) : "plaintext";
|
|
7356
|
-
return INodeHelper.createElementNode("code", {
|
|
7357
|
-
children: createCodeChildren(node.value),
|
|
7358
|
-
direction: "ltr",
|
|
7359
|
-
format: "",
|
|
7360
|
-
indent: 0,
|
|
7361
|
-
language,
|
|
7362
|
-
version: 1
|
|
7363
|
-
});
|
|
7364
|
-
});
|
|
7365
|
-
}
|
|
7366
|
-
};
|
|
7367
|
-
//#endregion
|
|
7368
9258
|
//#region src/headless/extract-media-from-editor-state.ts
|
|
7369
9259
|
const IMAGE_TYPES = new Set(["image", "block-image"]);
|
|
7370
9260
|
const FILE_TYPE = "file";
|
|
@@ -7502,10 +9392,15 @@ const preserveSerializedCodeTextInEditorData = (editorData) => ({
|
|
|
7502
9392
|
});
|
|
7503
9393
|
const DEFAULT_HEADLESS_EDITOR_PLUGINS = [
|
|
7504
9394
|
[CommonPlugin, { enableHotkey: false }],
|
|
9395
|
+
INodePlugin,
|
|
7505
9396
|
MarkdownPlugin,
|
|
7506
9397
|
[LinkPlugin, { enableHotkey: false }],
|
|
7507
9398
|
CodePlugin,
|
|
7508
|
-
|
|
9399
|
+
CodemirrorPlugin,
|
|
9400
|
+
ImagePlugin,
|
|
9401
|
+
FilePlugin,
|
|
9402
|
+
MathPlugin,
|
|
9403
|
+
MentionPlugin,
|
|
7509
9404
|
HRPlugin,
|
|
7510
9405
|
ListPlugin,
|
|
7511
9406
|
TablePlugin,
|