@lobehub/editor 1.16.3 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/index.d.ts +1 -0
- package/es/index.js +1 -0
- package/es/locale/index.d.ts +6 -0
- package/es/locale/index.js +6 -0
- package/es/plugins/common/plugin/index.js +19 -12
- package/es/plugins/markdown/command/index.d.ts +6 -0
- package/es/plugins/markdown/command/index.js +27 -0
- package/es/plugins/markdown/data-source/markdown/parse.js +8 -3
- package/es/plugins/markdown/index.d.ts +2 -0
- package/es/plugins/markdown/index.js +2 -0
- package/es/plugins/markdown/plugin/index.js +89 -48
- package/es/plugins/markdown/react/index.d.ts +3 -0
- package/es/plugins/markdown/react/index.js +70 -0
- package/es/types/kernel.d.ts +9 -1
- package/package.json +2 -1
package/es/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './plugins/hr';
|
|
|
8
8
|
export * from './plugins/image';
|
|
9
9
|
export * from './plugins/link';
|
|
10
10
|
export * from './plugins/list';
|
|
11
|
+
export * from './plugins/markdown';
|
|
11
12
|
export * from './plugins/math';
|
|
12
13
|
export * from './plugins/mention';
|
|
13
14
|
export * from './plugins/slash';
|
package/es/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./plugins/hr";
|
|
|
8
8
|
export * from "./plugins/image";
|
|
9
9
|
export * from "./plugins/link";
|
|
10
10
|
export * from "./plugins/list";
|
|
11
|
+
export * from "./plugins/markdown";
|
|
11
12
|
export * from "./plugins/math";
|
|
12
13
|
export * from "./plugins/mention";
|
|
13
14
|
export * from "./plugins/slash";
|
package/es/locale/index.d.ts
CHANGED
package/es/locale/index.js
CHANGED
|
@@ -16,6 +16,12 @@ export default {
|
|
|
16
16
|
placeholder: 'Enter link URL',
|
|
17
17
|
unlink: 'Unlink Link'
|
|
18
18
|
},
|
|
19
|
+
markdown: {
|
|
20
|
+
cancel: 'Cancel',
|
|
21
|
+
confirm: 'Confirm',
|
|
22
|
+
parseMessage: 'Convert to markdown format, existing content will be overwritten, confirm? (Auto close in 5 seconds)',
|
|
23
|
+
parseTitle: 'Parse Markdown'
|
|
24
|
+
},
|
|
19
25
|
math: {
|
|
20
26
|
placeholder: 'Enter TeX formula'
|
|
21
27
|
},
|
|
@@ -24,7 +24,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
|
|
|
24
24
|
import { registerDragonSupport } from '@lexical/dragon';
|
|
25
25
|
import { createEmptyHistoryState, registerHistory } from '@lexical/history';
|
|
26
26
|
import { $createHeadingNode, $createQuoteNode, $isHeadingNode, $isQuoteNode, HeadingNode, QuoteNode, registerRichText } from '@lexical/rich-text';
|
|
27
|
-
import { $createLineBreakNode, $createParagraphNode, $isTextNode } from 'lexical';
|
|
27
|
+
import { $createLineBreakNode, $createParagraphNode, $isTextNode, COMMAND_PRIORITY_HIGH, INSERT_LINE_BREAK_COMMAND, INSERT_PARAGRAPH_COMMAND } from 'lexical';
|
|
28
28
|
import { KernelPlugin } from "../../../editor-kernel/plugin";
|
|
29
29
|
import { IMarkdownShortCutService, isPunctuationChar } from "../../markdown";
|
|
30
30
|
import { registerCommands } from "../command";
|
|
@@ -82,7 +82,7 @@ export var CommonPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
82
82
|
// Parse markdown options
|
|
83
83
|
var markdownOption = (_this$config$markdown = (_this$config = this.config) === null || _this$config === void 0 ? void 0 : _this$config.markdownOption) !== null && _this$config$markdown !== void 0 ? _this$config$markdown : true;
|
|
84
84
|
var isMarkdownEnabled = markdownOption !== false;
|
|
85
|
-
var
|
|
85
|
+
var breakMark = isMarkdownEnabled ? '\n\n' : '\n';
|
|
86
86
|
|
|
87
87
|
// Determine which formats are enabled
|
|
88
88
|
var formats = {
|
|
@@ -189,11 +189,11 @@ export var CommonPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
189
189
|
markdownService.registerMarkdownShortCuts(textFormatShortcuts);
|
|
190
190
|
}
|
|
191
191
|
markdownService.registerMarkdownWriter('paragraph', function (ctx) {
|
|
192
|
-
ctx.wrap('',
|
|
192
|
+
ctx.wrap('', '\n');
|
|
193
193
|
});
|
|
194
194
|
markdownService.registerMarkdownWriter('quote', function (ctx, node) {
|
|
195
195
|
if ($isQuoteNode(node)) {
|
|
196
|
-
ctx.wrap('> ',
|
|
196
|
+
ctx.wrap('> ', breakMark);
|
|
197
197
|
}
|
|
198
198
|
});
|
|
199
199
|
markdownService.registerMarkdownWriter('heading', function (ctx, node) {
|
|
@@ -201,37 +201,37 @@ export var CommonPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
201
201
|
switch (node.getTag()) {
|
|
202
202
|
case 'h1':
|
|
203
203
|
{
|
|
204
|
-
ctx.wrap('# ',
|
|
204
|
+
ctx.wrap('# ', breakMark);
|
|
205
205
|
break;
|
|
206
206
|
}
|
|
207
207
|
case 'h2':
|
|
208
208
|
{
|
|
209
|
-
ctx.wrap('## ',
|
|
209
|
+
ctx.wrap('## ', breakMark);
|
|
210
210
|
break;
|
|
211
211
|
}
|
|
212
212
|
case 'h3':
|
|
213
213
|
{
|
|
214
|
-
ctx.wrap('### ',
|
|
214
|
+
ctx.wrap('### ', breakMark);
|
|
215
215
|
break;
|
|
216
216
|
}
|
|
217
217
|
case 'h4':
|
|
218
218
|
{
|
|
219
|
-
ctx.wrap('#### ',
|
|
219
|
+
ctx.wrap('#### ', breakMark);
|
|
220
220
|
break;
|
|
221
221
|
}
|
|
222
222
|
case 'h5':
|
|
223
223
|
{
|
|
224
|
-
ctx.wrap('##### ',
|
|
224
|
+
ctx.wrap('##### ', breakMark);
|
|
225
225
|
break;
|
|
226
226
|
}
|
|
227
227
|
case 'h6':
|
|
228
228
|
{
|
|
229
|
-
ctx.wrap('###### ',
|
|
229
|
+
ctx.wrap('###### ', breakMark);
|
|
230
230
|
break;
|
|
231
231
|
}
|
|
232
232
|
default:
|
|
233
233
|
{
|
|
234
|
-
ctx.wrap('',
|
|
234
|
+
ctx.wrap('', '\n');
|
|
235
235
|
break;
|
|
236
236
|
}
|
|
237
237
|
}
|
|
@@ -315,7 +315,14 @@ export var CommonPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
315
315
|
var _this$config2;
|
|
316
316
|
this.registerClears(registerRichText(editor), registerDragonSupport(editor), registerHistory(editor, createEmptyHistoryState(), 300), registerHeaderBackspace(editor), registerRichKeydown(editor, this.kernel, {
|
|
317
317
|
enableHotkey: (_this$config2 = this.config) === null || _this$config2 === void 0 ? void 0 : _this$config2.enableHotkey
|
|
318
|
-
}), registerCommands(editor), registerBreakLineClick(editor), registerCursorNode(editor), registerLastElement(editor)
|
|
318
|
+
}), registerCommands(editor), registerBreakLineClick(editor), registerCursorNode(editor), registerLastElement(editor),
|
|
319
|
+
// Convert soft line breaks (Shift+Enter) to hard line breaks (paragraph breaks)
|
|
320
|
+
// This allows breaking out of code blocks with Shift+Enter
|
|
321
|
+
editor.registerCommand(INSERT_LINE_BREAK_COMMAND, function () {
|
|
322
|
+
// Dispatch paragraph command instead of line break
|
|
323
|
+
editor.dispatchCommand(INSERT_PARAGRAPH_COMMAND, undefined);
|
|
324
|
+
return true; // Prevent default line break behavior
|
|
325
|
+
}, COMMAND_PRIORITY_HIGH));
|
|
319
326
|
this.registerMarkdown(this.kernel);
|
|
320
327
|
}
|
|
321
328
|
}, {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { LexicalEditor } from 'lexical';
|
|
2
|
+
import { MarkdownShortCutService } from '../service/shortcut';
|
|
3
|
+
export declare const INSERT_MARKDOWN_COMMAND: import("lexical").LexicalCommand<{
|
|
4
|
+
markdown: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function registerMarkdownCommand(editor: LexicalEditor, service: MarkdownShortCutService): () => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { $getSelection, COMMAND_PRIORITY_HIGH, createCommand } from 'lexical';
|
|
2
|
+
import { createDebugLogger } from "../../../utils/debug";
|
|
3
|
+
import { parseMarkdownToLexical } from "../data-source/markdown/parse";
|
|
4
|
+
import { $generateNodesFromSerializedNodes, $insertGeneratedNodes } from "../utils";
|
|
5
|
+
var logger = createDebugLogger('plugin', 'markdown');
|
|
6
|
+
export var INSERT_MARKDOWN_COMMAND = createCommand('INSERT_MARKDOWN_COMMAND');
|
|
7
|
+
export function registerMarkdownCommand(editor, service) {
|
|
8
|
+
return editor.registerCommand(INSERT_MARKDOWN_COMMAND, function (payload) {
|
|
9
|
+
var markdown = payload.markdown;
|
|
10
|
+
logger.debug('INSERT_MARKDOWN_COMMAND payload:', payload);
|
|
11
|
+
editor.update(function () {
|
|
12
|
+
try {
|
|
13
|
+
// Use the markdown data source to parse the content
|
|
14
|
+
var root = parseMarkdownToLexical(markdown, service.markdownReaders);
|
|
15
|
+
var selection = $getSelection();
|
|
16
|
+
var nodes = $generateNodesFromSerializedNodes(root.children);
|
|
17
|
+
logger.debug('INSERT_MARKDOWN_COMMAND nodes:', nodes);
|
|
18
|
+
$insertGeneratedNodes(editor, nodes, selection);
|
|
19
|
+
return true;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger.error('Failed to handle markdown paste:', error);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return false;
|
|
25
|
+
}, COMMAND_PRIORITY_HIGH // Priority
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -14,12 +14,13 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d
|
|
|
14
14
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
15
15
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
16
16
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
17
|
+
import { preprocessMarkdownContent } from '@lobehub/ui';
|
|
17
18
|
import { remark } from 'remark';
|
|
19
|
+
import remarkCjkFriendly from 'remark-cjk-friendly';
|
|
18
20
|
import remarkGfm from 'remark-gfm';
|
|
19
21
|
import remarkMath from 'remark-math';
|
|
20
22
|
import { INodeHelper } from "../../../../editor-kernel/inode/helper";
|
|
21
23
|
import { logger } from "../../utils/logger";
|
|
22
|
-
import remarkSupersub from "./supersub";
|
|
23
24
|
|
|
24
25
|
// 使用条件类型确保类型匹配
|
|
25
26
|
|
|
@@ -68,6 +69,10 @@ function convertMdastToLexical(node, index, ctx) {
|
|
|
68
69
|
var htmlStack = []; // 当前循环是否包含 HTML 标签
|
|
69
70
|
_children = node.children.reduce(function (ret, child, index) {
|
|
70
71
|
if (child.type === 'html') {
|
|
72
|
+
var isComment = child.value.startsWith('<!--') && child.value.endsWith('-->');
|
|
73
|
+
if (isComment) {
|
|
74
|
+
return ret;
|
|
75
|
+
}
|
|
71
76
|
var tag = child.value.replaceAll(/^<\/?|>$/g, '');
|
|
72
77
|
var isEndTag = child.value.startsWith('</');
|
|
73
78
|
if (selfClosingHtmlTags.has(tag)) {
|
|
@@ -221,9 +226,9 @@ function registerDefaultReaders(markdownReaders) {
|
|
|
221
226
|
}
|
|
222
227
|
export function parseMarkdownToLexical(markdown) {
|
|
223
228
|
var markdownReaders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
224
|
-
var ast = remark().use(
|
|
229
|
+
var ast = remark().use(remarkCjkFriendly).use(remarkMath).use([[remarkGfm, {
|
|
225
230
|
singleTilde: false
|
|
226
|
-
}]]).parse(markdown);
|
|
231
|
+
}]]).parse(preprocessMarkdownContent(markdown));
|
|
227
232
|
logger.debug('Parsed MDAST:', ast);
|
|
228
233
|
var ctx = new MarkdownContext(ast);
|
|
229
234
|
registerDefaultReaders(markdownReaders);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
export { INSERT_MARKDOWN_COMMAND } from './command';
|
|
1
2
|
export { MarkdownPlugin } from './plugin';
|
|
3
|
+
export { default as ReactMarkdownPlugin } from './react';
|
|
2
4
|
export type { MARKDOWN_READER_LEVEL } from './service/shortcut';
|
|
3
5
|
export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, } from './service/shortcut';
|
|
4
6
|
export { isPunctuationChar } from './utils';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { INSERT_MARKDOWN_COMMAND } from "./command";
|
|
1
2
|
export { MarkdownPlugin } from "./plugin";
|
|
3
|
+
export { default as ReactMarkdownPlugin } from "./react";
|
|
2
4
|
export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX } from "./service/shortcut";
|
|
3
5
|
export { isPunctuationChar } from "./utils";
|
|
@@ -17,10 +17,10 @@ import { $isCodeNode } from '@lexical/code';
|
|
|
17
17
|
import { $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, HISTORIC_TAG, KEY_ENTER_COMMAND, PASTE_COMMAND } from 'lexical';
|
|
18
18
|
import { KernelPlugin } from "../../../editor-kernel/plugin";
|
|
19
19
|
import { createDebugLogger } from "../../../utils/debug";
|
|
20
|
+
import { registerMarkdownCommand } from "../command";
|
|
20
21
|
import MarkdownDataSource from "../data-source/markdown-data-source";
|
|
21
|
-
import { parseMarkdownToLexical } from "../data-source/markdown/parse";
|
|
22
22
|
import { IMarkdownShortCutService, MarkdownShortCutService } from "../service/shortcut";
|
|
23
|
-
import {
|
|
23
|
+
import { canContainTransformableMarkdown } from "../utils";
|
|
24
24
|
export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
25
25
|
_inherits(MarkdownPlugin, _KernelPlugin);
|
|
26
26
|
var _super = _createSuper(MarkdownPlugin);
|
|
@@ -133,50 +133,92 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
133
133
|
|
|
134
134
|
// Check if markdown paste formatting is enabled (default: true)
|
|
135
135
|
var enablePasteMarkdown = (_this2$config$enableP = (_this2$config = _this2.config) === null || _this2$config === void 0 ? void 0 : _this2$config.enablePasteMarkdown) !== null && _this2$config$enableP !== void 0 ? _this2$config$enableP : true;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
editor.update(function () {
|
|
142
|
-
var selection = $getSelection();
|
|
143
|
-
if (!$isRangeSelection(selection)) return;
|
|
136
|
+
_this2.logger.debug('paste content analysis:', {
|
|
137
|
+
enablePasteMarkdown: enablePasteMarkdown,
|
|
138
|
+
hasHTML: !!(html && html.trim()),
|
|
139
|
+
text: text.slice(0, 100) + (text.length > 100 ? '...' : '')
|
|
140
|
+
});
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
142
|
+
// Always force plain text paste first (prevents HTML formatting issues)
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
event.stopPropagation();
|
|
145
|
+
editor.update(function () {
|
|
146
|
+
var selection = $getSelection();
|
|
147
|
+
if (!$isRangeSelection(selection)) return;
|
|
150
148
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
149
|
+
// Insert plain text
|
|
150
|
+
selection.insertText(text);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// If markdown formatting is disabled, we're done
|
|
154
|
+
if (!enablePasteMarkdown) {
|
|
155
|
+
_this2.logger.debug('markdown formatting disabled, plain text inserted');
|
|
156
|
+
return true;
|
|
156
157
|
}
|
|
157
158
|
|
|
158
|
-
//
|
|
159
|
+
// Check if the pasted plain text contains markdown patterns
|
|
159
160
|
var hasMarkdownContent = _this2.detectMarkdownContent(text);
|
|
160
|
-
_this2.logger.debug('paste content analysis:', {
|
|
161
|
-
hasHTML: false,
|
|
162
|
-
hasMarkdown: hasMarkdownContent,
|
|
163
|
-
markdownPatterns: _this2.getMarkdownPatterns(text),
|
|
164
|
-
text: text.slice(0, 100) + (text.length > 100 ? '...' : '')
|
|
165
|
-
});
|
|
166
161
|
if (hasMarkdownContent) {
|
|
167
|
-
//
|
|
168
|
-
|
|
162
|
+
// Markdown detected - show confirmation dialog
|
|
163
|
+
_this2.logger.debug('markdown patterns detected:', _this2.getMarkdownPatterns(text));
|
|
164
|
+
setTimeout(function () {
|
|
165
|
+
_this2.kernel.emit('markdownParse', {
|
|
166
|
+
cacheState: editor.getEditorState(),
|
|
167
|
+
markdown: text
|
|
168
|
+
});
|
|
169
|
+
}, 10);
|
|
170
|
+
} else {
|
|
171
|
+
// No markdown detected - plain text is already inserted
|
|
172
|
+
_this2.logger.debug('no markdown patterns detected, keeping as plain text');
|
|
169
173
|
}
|
|
170
|
-
return
|
|
174
|
+
return true; // Command handled
|
|
171
175
|
}, COMMAND_PRIORITY_CRITICAL));
|
|
176
|
+
this.register(registerMarkdownCommand(editor, this.service));
|
|
172
177
|
}
|
|
173
178
|
|
|
174
179
|
/**
|
|
175
180
|
* Detect if text contains markdown patterns
|
|
181
|
+
* Returns false if content is likely code (JSON, HTML, SQL, etc.)
|
|
176
182
|
*/
|
|
177
183
|
}, {
|
|
178
184
|
key: "detectMarkdownContent",
|
|
179
185
|
value: function detectMarkdownContent(text) {
|
|
186
|
+
var trimmed = text.trim();
|
|
187
|
+
|
|
188
|
+
// Check if content is JSON
|
|
189
|
+
if (trimmed.startsWith('{') && trimmed.endsWith('}') || trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
190
|
+
try {
|
|
191
|
+
JSON.parse(trimmed);
|
|
192
|
+
this.logger.debug('content is valid JSON, not treating as markdown');
|
|
193
|
+
return false;
|
|
194
|
+
} catch (_unused) {
|
|
195
|
+
// Not valid JSON, continue checking
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check if content has significant HTML structure
|
|
200
|
+
var htmlTagPattern = /<[a-z][\S\s]*?>/gi;
|
|
201
|
+
var htmlMatches = text.match(htmlTagPattern);
|
|
202
|
+
if (htmlMatches && htmlMatches.length > 5) {
|
|
203
|
+
// More than 5 HTML tags suggests this is HTML content, not markdown
|
|
204
|
+
this.logger.debug('content has significant HTML structure, not treating as markdown');
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Check if content looks like code (SQL, XML, etc.)
|
|
209
|
+
// Common patterns: SQL keywords, XML declarations, file paths
|
|
210
|
+
var codePatterns = [/^\s*(select|insert|update|delete|create|alter|drop)\s+/im,
|
|
211
|
+
// SQL
|
|
212
|
+
/^\s*<\?xml/i,
|
|
213
|
+
// XML declaration
|
|
214
|
+
/^[a-z]:\\|^\/[a-z]/im // File paths (Windows/Unix)
|
|
215
|
+
];
|
|
216
|
+
if (codePatterns.some(function (pattern) {
|
|
217
|
+
return pattern.test(text);
|
|
218
|
+
})) {
|
|
219
|
+
this.logger.debug('content looks like code, not treating as markdown');
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
180
222
|
var markdownPatterns = [
|
|
181
223
|
// Headers
|
|
182
224
|
/^#{1,6}\s+/m,
|
|
@@ -260,24 +302,23 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
260
302
|
});
|
|
261
303
|
}
|
|
262
304
|
|
|
263
|
-
/**
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
305
|
+
// /**
|
|
306
|
+
// * Handle markdown paste by parsing and inserting as structured content
|
|
307
|
+
// */
|
|
308
|
+
// private handleMarkdownPaste(editor: LexicalEditor, text: string): boolean {
|
|
309
|
+
// try {
|
|
310
|
+
// // Use the markdown data source to parse the content
|
|
311
|
+
// const root = parseMarkdownToLexical(text, this.service.markdownReaders);
|
|
312
|
+
// const selection = $getSelection();
|
|
313
|
+
// const nodes = $generateNodesFromSerializedNodes(root.children);
|
|
314
|
+
// $insertGeneratedNodes(editor, nodes, selection!);
|
|
315
|
+
// return true;
|
|
316
|
+
// } catch (error) {
|
|
317
|
+
// this.logger.error('Failed to handle markdown paste:', error);
|
|
318
|
+
// }
|
|
319
|
+
|
|
320
|
+
// return false;
|
|
321
|
+
// }
|
|
281
322
|
}]);
|
|
282
323
|
return MarkdownPlugin;
|
|
283
324
|
}(KernelPlugin), _defineProperty(_class, "pluginName", 'MarkdownPlugin'), _class);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
4
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
5
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
6
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
7
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
8
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
9
|
+
import { Button } from '@lobehub/ui';
|
|
10
|
+
import { Space, notification } from 'antd';
|
|
11
|
+
import { UNDO_COMMAND } from 'lexical';
|
|
12
|
+
import { useLayoutEffect } from 'react';
|
|
13
|
+
import { useLexicalComposerContext } from "../../../editor-kernel/react";
|
|
14
|
+
import { useTranslation } from "../../../editor-kernel/react/useTranslation";
|
|
15
|
+
import { INSERT_MARKDOWN_COMMAND } from "../command";
|
|
16
|
+
import { MarkdownPlugin } from "../plugin";
|
|
17
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
|
+
var ReactMarkdownPlugin = function ReactMarkdownPlugin() {
|
|
20
|
+
var _useLexicalComposerCo = useLexicalComposerContext(),
|
|
21
|
+
_useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
|
|
22
|
+
editor = _useLexicalComposerCo2[0];
|
|
23
|
+
var _notification$useNoti = notification.useNotification(),
|
|
24
|
+
_notification$useNoti2 = _slicedToArray(_notification$useNoti, 2),
|
|
25
|
+
api = _notification$useNoti2[0],
|
|
26
|
+
contextHolder = _notification$useNoti2[1];
|
|
27
|
+
var t = useTranslation();
|
|
28
|
+
useLayoutEffect(function () {
|
|
29
|
+
editor.registerPlugin(MarkdownPlugin);
|
|
30
|
+
var handleEvent = function handleEvent(_ref) {
|
|
31
|
+
var markdown = _ref.markdown;
|
|
32
|
+
var key = "open".concat(Date.now());
|
|
33
|
+
var actions = /*#__PURE__*/_jsxs(Space, {
|
|
34
|
+
children: [/*#__PURE__*/_jsx(Button, {
|
|
35
|
+
onClick: function onClick() {
|
|
36
|
+
return api.destroy();
|
|
37
|
+
},
|
|
38
|
+
size: "small",
|
|
39
|
+
children: t('markdown.cancel')
|
|
40
|
+
}), /*#__PURE__*/_jsx(Button, {
|
|
41
|
+
onClick: function onClick() {
|
|
42
|
+
editor.dispatchCommand(UNDO_COMMAND, undefined);
|
|
43
|
+
editor.dispatchCommand(INSERT_MARKDOWN_COMMAND, {
|
|
44
|
+
markdown: markdown
|
|
45
|
+
});
|
|
46
|
+
api.destroy();
|
|
47
|
+
},
|
|
48
|
+
size: "small",
|
|
49
|
+
type: "primary",
|
|
50
|
+
children: t('markdown.confirm')
|
|
51
|
+
})]
|
|
52
|
+
});
|
|
53
|
+
api.open({
|
|
54
|
+
actions: actions,
|
|
55
|
+
description: t('markdown.parseMessage'),
|
|
56
|
+
duration: 5,
|
|
57
|
+
key: key,
|
|
58
|
+
message: t('markdown.parseTitle'),
|
|
59
|
+
showProgress: true
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
editor.on('markdownParse', handleEvent);
|
|
63
|
+
return function () {
|
|
64
|
+
editor.off('markdownParse', handleEvent);
|
|
65
|
+
};
|
|
66
|
+
}, [editor]);
|
|
67
|
+
return contextHolder;
|
|
68
|
+
};
|
|
69
|
+
ReactMarkdownPlugin.displayName = 'ReactMarkdownPlugin';
|
|
70
|
+
export default ReactMarkdownPlugin;
|
package/es/types/kernel.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CommandListener, CommandListenerPriority, CommandPayloadType, DecoratorNode, LexicalCommand, LexicalEditor, LexicalNodeConfig } from 'lexical';
|
|
1
|
+
import type { CommandListener, CommandListenerPriority, CommandPayloadType, DecoratorNode, EditorState, LexicalCommand, LexicalEditor, LexicalNodeConfig } from 'lexical';
|
|
2
2
|
import type DataSource from "../editor-kernel/data-source";
|
|
3
3
|
import { HotkeyId } from "./hotkey";
|
|
4
4
|
import { HotkeyOptions, HotkeysEvent } from "../utils/hotkey/registerHotkey";
|
|
@@ -23,6 +23,13 @@ export interface IKernelEventMap {
|
|
|
23
23
|
* @returns
|
|
24
24
|
*/
|
|
25
25
|
initialized: (editor: LexicalEditor) => void;
|
|
26
|
+
/**
|
|
27
|
+
* handle markdown parse event
|
|
28
|
+
*/
|
|
29
|
+
markdownParse: (params: {
|
|
30
|
+
cacheState: EditorState;
|
|
31
|
+
markdown: string;
|
|
32
|
+
}) => void;
|
|
26
33
|
}
|
|
27
34
|
/**
|
|
28
35
|
* External API
|
|
@@ -175,6 +182,7 @@ export interface IEditor {
|
|
|
175
182
|
* API provided to plugins
|
|
176
183
|
*/
|
|
177
184
|
export interface IEditorKernel extends IEditor {
|
|
185
|
+
emit<T extends keyof IKernelEventMap>(event: T, params: Parameters<IKernelEventMap[T]>[0]): void;
|
|
178
186
|
/**
|
|
179
187
|
* Get editor Node decorator for specific Node rendering
|
|
180
188
|
* @param name
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.1",
|
|
4
4
|
"description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lobehub",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"react-error-boundary": "^6.0.0",
|
|
57
57
|
"react-layout-kit": "^2.0.0",
|
|
58
58
|
"react-merge-refs": "^3.0.2",
|
|
59
|
+
"remark-cjk-friendly": "^1.2.1",
|
|
59
60
|
"remark-supersub": "^1.0.0",
|
|
60
61
|
"shiki": "^3.9.2",
|
|
61
62
|
"ts-key-enum": "^3.0.13",
|