@lobehub/editor 1.11.0 → 1.13.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/editor-kernel/inode/helper.d.ts +9 -6
- package/es/editor-kernel/inode/helper.js +27 -0
- package/es/editor-kernel/inode/text-node.d.ts +2 -9
- package/es/plugins/code/plugin/index.d.ts +1 -1
- package/es/plugins/code/plugin/index.js +12 -1
- package/es/plugins/codeblock/command/index.d.ts +6 -0
- package/es/plugins/codeblock/command/index.js +1 -0
- package/es/plugins/codeblock/plugin/CodeHighlighterShiki.d.ts +7 -0
- package/es/plugins/codeblock/plugin/CodeHighlighterShiki.js +43 -2
- package/es/plugins/codeblock/plugin/FacadeShiki.d.ts +8 -1
- package/es/plugins/codeblock/plugin/FacadeShiki.js +95 -6
- package/es/plugins/codeblock/plugin/index.js +74 -29
- package/es/plugins/common/data-source/json-data-source.d.ts +2 -2
- package/es/plugins/common/data-source/json-data-source.js +2 -10
- package/es/plugins/common/index.d.ts +1 -1
- package/es/plugins/common/index.js +1 -1
- package/es/plugins/common/node/cursor.d.ts +3 -1
- package/es/plugins/common/node/cursor.js +9 -0
- package/es/plugins/common/plugin/index.d.ts +1 -1
- package/es/plugins/common/plugin/index.js +28 -1
- package/es/plugins/common/plugin/mdReader.d.ts +2 -0
- package/es/plugins/common/plugin/mdReader.js +84 -0
- package/es/plugins/common/react/ReactPlainText.d.ts +1 -1
- package/es/plugins/common/utils/index.d.ts +2 -2
- package/es/plugins/hr/plugin/index.js +26 -22
- package/es/plugins/link/plugin/index.js +42 -26
- package/es/plugins/list/plugin/index.js +121 -63
- package/es/plugins/list/utils/index.d.ts +3 -3
- package/es/plugins/markdown/data-source/markdown/parse.d.ts +21 -0
- package/es/plugins/markdown/data-source/markdown/parse.js +231 -0
- package/es/plugins/markdown/data-source/markdown/supersub.d.ts +1 -0
- package/es/plugins/markdown/data-source/markdown/supersub.js +14 -0
- package/es/plugins/markdown/data-source/markdown-data-source.d.ts +4 -4
- package/es/plugins/markdown/data-source/markdown-data-source.js +8 -2
- package/es/plugins/markdown/index.d.ts +2 -1
- package/es/plugins/markdown/index.js +1 -1
- package/es/plugins/markdown/plugin/index.js +135 -2
- package/es/plugins/markdown/service/shortcut.d.ts +19 -85
- package/es/plugins/markdown/service/shortcut.js +49 -293
- package/es/plugins/markdown/service/transformers.d.ts +60 -0
- package/es/plugins/markdown/service/transformers.js +286 -0
- package/es/plugins/markdown/utils/index.d.ts +45 -1
- package/es/plugins/markdown/utils/index.js +147 -1
- package/es/plugins/markdown/utils/logger.d.ts +7 -0
- package/es/plugins/markdown/utils/logger.js +2 -0
- package/es/plugins/math/plugin/index.js +64 -45
- package/es/plugins/mention/index.d.ts +1 -0
- package/es/plugins/mention/plugin/index.d.ts +4 -1
- package/es/plugins/mention/plugin/index.js +27 -11
- package/es/plugins/table/plugin/index.js +71 -26
- package/es/react/hooks/useEditorState/index.js +43 -21
- package/es/types/global.d.ts +64 -0
- package/package.json +2 -1
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
5
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
6
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
7
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
8
|
+
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
|
|
9
|
+
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); }
|
|
10
|
+
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; }
|
|
11
|
+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
12
|
+
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
13
|
+
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
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
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
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 { remark } from 'remark';
|
|
18
|
+
import remarkGfm from 'remark-gfm';
|
|
19
|
+
import remarkMath from 'remark-math';
|
|
20
|
+
import { INodeHelper } from "../../../../editor-kernel/inode/helper";
|
|
21
|
+
import { logger } from "../../utils/logger";
|
|
22
|
+
import remarkSupersub from "./supersub";
|
|
23
|
+
|
|
24
|
+
// 使用条件类型确保类型匹配
|
|
25
|
+
|
|
26
|
+
var selfClosingHtmlTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
27
|
+
var MarkdownContext = /*#__PURE__*/function () {
|
|
28
|
+
function MarkdownContext(root) {
|
|
29
|
+
_classCallCheck(this, MarkdownContext);
|
|
30
|
+
_defineProperty(this, "stack", []);
|
|
31
|
+
this.root = root;
|
|
32
|
+
}
|
|
33
|
+
_createClass(MarkdownContext, [{
|
|
34
|
+
key: "push",
|
|
35
|
+
value: function push(html) {
|
|
36
|
+
this.stack.push(html);
|
|
37
|
+
}
|
|
38
|
+
}, {
|
|
39
|
+
key: "isReadingHTML",
|
|
40
|
+
get: function get() {
|
|
41
|
+
return this.stack.length > 0;
|
|
42
|
+
}
|
|
43
|
+
}, {
|
|
44
|
+
key: "last",
|
|
45
|
+
get: function get() {
|
|
46
|
+
return this.stack.at(-1);
|
|
47
|
+
}
|
|
48
|
+
}, {
|
|
49
|
+
key: "pop",
|
|
50
|
+
value: function pop() {
|
|
51
|
+
return this.stack.pop();
|
|
52
|
+
}
|
|
53
|
+
}]);
|
|
54
|
+
return MarkdownContext;
|
|
55
|
+
}();
|
|
56
|
+
function convertMdastToLexical(node, index, ctx) {
|
|
57
|
+
var markdownReaders = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
|
|
58
|
+
switch (node.type) {
|
|
59
|
+
case 'text':
|
|
60
|
+
{
|
|
61
|
+
var textNode = INodeHelper.createTextNode(node.value);
|
|
62
|
+
return textNode;
|
|
63
|
+
}
|
|
64
|
+
default:
|
|
65
|
+
{
|
|
66
|
+
if (markdownReaders[node.type]) {
|
|
67
|
+
var _children = [];
|
|
68
|
+
if ('children' in node && Array.isArray(node.children)) {
|
|
69
|
+
var htmlStack = []; // 当前循环是否包含 HTML 标签
|
|
70
|
+
_children = node.children.reduce(function (ret, child, index) {
|
|
71
|
+
if (child.type === 'html') {
|
|
72
|
+
var tag = child.value.replaceAll(/^<\/?|>$/g, '');
|
|
73
|
+
var isEndTag = child.value.startsWith('</');
|
|
74
|
+
if (selfClosingHtmlTags.has(tag)) {
|
|
75
|
+
// Self-closing tag
|
|
76
|
+
var _reader = markdownReaders['html'];
|
|
77
|
+
if (Array.isArray(_reader)) {
|
|
78
|
+
var _iterator = _createForOfIteratorHelper(_reader),
|
|
79
|
+
_step;
|
|
80
|
+
try {
|
|
81
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
82
|
+
var element = _step.value;
|
|
83
|
+
var inode = element(child, [], index);
|
|
84
|
+
if (inode) {
|
|
85
|
+
ret.push(inode);
|
|
86
|
+
return ret;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
_iterator.e(err);
|
|
91
|
+
} finally {
|
|
92
|
+
_iterator.f();
|
|
93
|
+
}
|
|
94
|
+
} else if (typeof _reader === 'function') {
|
|
95
|
+
var _inode = _reader(child, [], index);
|
|
96
|
+
if (_inode) {
|
|
97
|
+
ret.push(_inode);
|
|
98
|
+
return ret;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return ret;
|
|
102
|
+
}
|
|
103
|
+
if (isEndTag) {
|
|
104
|
+
var top = ctx.pop();
|
|
105
|
+
htmlStack.pop();
|
|
106
|
+
if ((top === null || top === void 0 ? void 0 : top.tag) !== tag) {
|
|
107
|
+
logger.warn('HTML tag mismatch:', tag);
|
|
108
|
+
ret.push.apply(ret, _toConsumableArray((top === null || top === void 0 ? void 0 : top.children) || []));
|
|
109
|
+
return ret;
|
|
110
|
+
}
|
|
111
|
+
var _reader2 = markdownReaders['html'];
|
|
112
|
+
var _children2 = top.children.flat().filter(Boolean) || [];
|
|
113
|
+
if (Array.isArray(_reader2)) {
|
|
114
|
+
var _iterator2 = _createForOfIteratorHelper(_reader2),
|
|
115
|
+
_step2;
|
|
116
|
+
try {
|
|
117
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
118
|
+
var _element = _step2.value;
|
|
119
|
+
var _inode2 = _element(top.node, _children2, index);
|
|
120
|
+
if (_inode2) {
|
|
121
|
+
ret.push(_inode2);
|
|
122
|
+
return ret;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} catch (err) {
|
|
126
|
+
_iterator2.e(err);
|
|
127
|
+
} finally {
|
|
128
|
+
_iterator2.f();
|
|
129
|
+
}
|
|
130
|
+
} else if (typeof _reader2 === 'function') {
|
|
131
|
+
var _inode3 = _reader2(top.node, _children2, index);
|
|
132
|
+
if (_inode3) {
|
|
133
|
+
ret.push(_inode3);
|
|
134
|
+
return ret;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (top) {
|
|
138
|
+
ret.push.apply(ret, _toConsumableArray(top.children));
|
|
139
|
+
}
|
|
140
|
+
return ret;
|
|
141
|
+
}
|
|
142
|
+
var htmlStackItem = {
|
|
143
|
+
children: [],
|
|
144
|
+
index: index,
|
|
145
|
+
isEndTag: isEndTag,
|
|
146
|
+
node: child,
|
|
147
|
+
tag: tag
|
|
148
|
+
};
|
|
149
|
+
htmlStack.push(htmlStackItem);
|
|
150
|
+
ctx.push(htmlStackItem);
|
|
151
|
+
return ret;
|
|
152
|
+
}
|
|
153
|
+
if (htmlStack.length > 0) {
|
|
154
|
+
var _top = ctx.last;
|
|
155
|
+
if (_top) {
|
|
156
|
+
_top.children.push(convertMdastToLexical(child, index, ctx, markdownReaders));
|
|
157
|
+
}
|
|
158
|
+
return ret;
|
|
159
|
+
}
|
|
160
|
+
ret.push(convertMdastToLexical(child, index, ctx, markdownReaders));
|
|
161
|
+
return ret;
|
|
162
|
+
}, []).filter(Boolean).flat();
|
|
163
|
+
}
|
|
164
|
+
var reader = markdownReaders[node.type];
|
|
165
|
+
if (Array.isArray(reader)) {
|
|
166
|
+
var _iterator3 = _createForOfIteratorHelper(reader),
|
|
167
|
+
_step3;
|
|
168
|
+
try {
|
|
169
|
+
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
|
|
170
|
+
var element = _step3.value;
|
|
171
|
+
var inode = element(node, _children, index);
|
|
172
|
+
if (inode) {
|
|
173
|
+
return inode;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
} catch (err) {
|
|
177
|
+
_iterator3.e(err);
|
|
178
|
+
} finally {
|
|
179
|
+
_iterator3.f();
|
|
180
|
+
}
|
|
181
|
+
} else if (typeof reader === 'function') {
|
|
182
|
+
var _inode4 = reader(node, _children, index);
|
|
183
|
+
if (_inode4) {
|
|
184
|
+
return _inode4;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Fallback for unsupported nodes
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function registerDefaultReaders(markdownReaders) {
|
|
195
|
+
if (!markdownReaders['root']) {
|
|
196
|
+
markdownReaders['root'] = function (node, children) {
|
|
197
|
+
return _objectSpread(_objectSpread({}, INodeHelper.createRootNode()), {}, {
|
|
198
|
+
children: children
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (!markdownReaders['paragraph']) {
|
|
203
|
+
markdownReaders['paragraph'] = function (node, children) {
|
|
204
|
+
return _objectSpread(_objectSpread({}, INodeHelper.createParagraph()), {}, {
|
|
205
|
+
children: children
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (!markdownReaders['heading']) {
|
|
210
|
+
markdownReaders['heading'] = function (node, children) {
|
|
211
|
+
var headingType = "h".concat(Math.min(Math.max(node.depth, 1), 6));
|
|
212
|
+
return INodeHelper.createElementNode('heading', {
|
|
213
|
+
children: children,
|
|
214
|
+
direction: 'ltr',
|
|
215
|
+
format: '',
|
|
216
|
+
indent: 0,
|
|
217
|
+
tag: headingType
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export function parseMarkdownToLexical(markdown) {
|
|
223
|
+
var markdownReaders = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
224
|
+
var ast = remark().use(remarkMath).use(remarkSupersub).use([[remarkGfm, {
|
|
225
|
+
singleTilde: false
|
|
226
|
+
}]]).parse(markdown);
|
|
227
|
+
logger.debug('Parsed MDAST:', ast);
|
|
228
|
+
var ctx = new MarkdownContext(ast);
|
|
229
|
+
registerDefaultReaders(markdownReaders);
|
|
230
|
+
return convertMdastToLexical(ast, 0, ctx, markdownReaders);
|
|
231
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function remarkSupersub(): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-this-alias */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-invalid-this */
|
|
3
|
+
/* eslint-disable unused-imports/no-unused-vars */
|
|
4
|
+
import supersub from 'remark-supersub';
|
|
5
|
+
export default function remarkSupersub() {
|
|
6
|
+
// @ts-expect-error: TS is wrong about `this`.
|
|
7
|
+
// eslint-disable-next-line unicorn/no-this-assignment
|
|
8
|
+
var self = /** @type {Processor} */this;
|
|
9
|
+
var data = self.data();
|
|
10
|
+
var fromMarkdownExtensions = data.fromMarkdownExtensions || (data.fromMarkdownExtensions = []);
|
|
11
|
+
fromMarkdownExtensions.push({
|
|
12
|
+
transforms: [supersub()]
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { LexicalEditor } from 'lexical';
|
|
1
|
+
import type { LexicalEditor } from 'lexical';
|
|
2
2
|
import { DataSource } from "../../../editor-kernel";
|
|
3
|
-
import { IWriteOptions } from "../../../editor-kernel/data-source";
|
|
4
|
-
import { MarkdownShortCutService } from '../service/shortcut';
|
|
3
|
+
import type { IWriteOptions } from "../../../editor-kernel/data-source";
|
|
4
|
+
import type { MarkdownShortCutService } from '../service/shortcut';
|
|
5
5
|
export default class MarkdownDataSource extends DataSource {
|
|
6
6
|
protected dataType: string;
|
|
7
7
|
protected markdownService: MarkdownShortCutService;
|
|
8
8
|
constructor(dataType: string, markdownService: MarkdownShortCutService);
|
|
9
|
-
read(): void;
|
|
9
|
+
read(editor: LexicalEditor, data: string): void;
|
|
10
10
|
write(editor: LexicalEditor, options?: IWriteOptions): any;
|
|
11
11
|
}
|
|
@@ -29,7 +29,9 @@ import { $isTableSelection } from '@lexical/table';
|
|
|
29
29
|
import { $getCharacterOffsets, $getNodeByKey, $getRoot, $getSelection, $isElementNode, $isRangeSelection, $isTextNode } from 'lexical';
|
|
30
30
|
import { DataSource } from "../../../editor-kernel";
|
|
31
31
|
import { INodeHelper } from "../../../editor-kernel/inode/helper";
|
|
32
|
+
import { logger } from "../utils/logger";
|
|
32
33
|
import { MarkdownWriterContext } from "./markdown-writer-context";
|
|
34
|
+
import { parseMarkdownToLexical } from "./markdown/parse";
|
|
33
35
|
var MarkdownDataSource = /*#__PURE__*/function (_DataSource) {
|
|
34
36
|
_inherits(MarkdownDataSource, _DataSource);
|
|
35
37
|
var _super = _createSuper(MarkdownDataSource);
|
|
@@ -43,8 +45,12 @@ var MarkdownDataSource = /*#__PURE__*/function (_DataSource) {
|
|
|
43
45
|
}
|
|
44
46
|
_createClass(MarkdownDataSource, [{
|
|
45
47
|
key: "read",
|
|
46
|
-
value: function read() {
|
|
47
|
-
|
|
48
|
+
value: function read(editor, data) {
|
|
49
|
+
var inode = {
|
|
50
|
+
root: parseMarkdownToLexical(data, this.markdownService.markdownReaders)
|
|
51
|
+
};
|
|
52
|
+
logger.debug('Parsed Lexical State:', inode);
|
|
53
|
+
editor.setEditorState(editor.parseEditorState(inode));
|
|
48
54
|
}
|
|
49
55
|
}, {
|
|
50
56
|
key: "write",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { MarkdownPlugin } from './plugin';
|
|
2
|
-
export {
|
|
2
|
+
export type { MARKDOWN_READER_LEVEL } from './service/shortcut';
|
|
3
|
+
export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, } from './service/shortcut';
|
|
3
4
|
export { isPunctuationChar } from './utils';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { MarkdownPlugin } from "./plugin";
|
|
2
|
-
export { IMarkdownShortCutService } from "./service/shortcut";
|
|
2
|
+
export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX } from "./service/shortcut";
|
|
3
3
|
export { isPunctuationChar } from "./utils";
|
|
@@ -14,11 +14,12 @@ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key i
|
|
|
14
14
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
15
15
|
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); }
|
|
16
16
|
import { $isCodeNode } from '@lexical/code';
|
|
17
|
-
import { $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, COLLABORATION_TAG, COMMAND_PRIORITY_CRITICAL, HISTORIC_TAG, KEY_ENTER_COMMAND } from 'lexical';
|
|
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 MarkdownDataSource from "../data-source/markdown-data-source";
|
|
20
|
+
import { parseMarkdownToLexical } from "../data-source/markdown/parse";
|
|
20
21
|
import { IMarkdownShortCutService, MarkdownShortCutService } from "../service/shortcut";
|
|
21
|
-
import { canContainTransformableMarkdown } from "../utils";
|
|
22
|
+
import { $generateNodesFromSerializedNodes, $insertGeneratedNodes, canContainTransformableMarkdown } from "../utils";
|
|
22
23
|
|
|
23
24
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
24
25
|
|
|
@@ -117,6 +118,138 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
|
|
|
117
118
|
}
|
|
118
119
|
return false;
|
|
119
120
|
}, COMMAND_PRIORITY_CRITICAL));
|
|
121
|
+
this.register(editor.registerCommand(PASTE_COMMAND, function (event) {
|
|
122
|
+
if (!(event instanceof ClipboardEvent)) return false;
|
|
123
|
+
var clipboardData = event.clipboardData;
|
|
124
|
+
if (!clipboardData) return false;
|
|
125
|
+
|
|
126
|
+
// Get plain text content
|
|
127
|
+
var text = clipboardData.getData('text/plain');
|
|
128
|
+
var html = clipboardData.getData('text/html');
|
|
129
|
+
if (!text) return false;
|
|
130
|
+
|
|
131
|
+
// Check if content contains markdown patterns
|
|
132
|
+
var hasMarkdownContent = _this2.detectMarkdownContent(text);
|
|
133
|
+
console.log('paste content analysis:', {
|
|
134
|
+
hasHTML: !!html,
|
|
135
|
+
hasMarkdown: hasMarkdownContent,
|
|
136
|
+
markdownPatterns: _this2.getMarkdownPatterns(text),
|
|
137
|
+
text: text.slice(0, 100) + (text.length > 100 ? '...' : '')
|
|
138
|
+
});
|
|
139
|
+
if (hasMarkdownContent) {
|
|
140
|
+
// Handle markdown paste
|
|
141
|
+
return _this2.handleMarkdownPaste(editor, text);
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}, COMMAND_PRIORITY_CRITICAL));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Detect if text contains markdown patterns
|
|
149
|
+
*/
|
|
150
|
+
}, {
|
|
151
|
+
key: "detectMarkdownContent",
|
|
152
|
+
value: function detectMarkdownContent(text) {
|
|
153
|
+
var markdownPatterns = [
|
|
154
|
+
// Headers
|
|
155
|
+
/^#{1,6}\s+/m,
|
|
156
|
+
// Bold/italic
|
|
157
|
+
/\*{1,2}[^*]+\*{1,2}/, /__?[^_]+__?/,
|
|
158
|
+
// Code blocks
|
|
159
|
+
/```[\S\s]*```/,
|
|
160
|
+
// Inline code
|
|
161
|
+
/`[^`]+`/,
|
|
162
|
+
// Links
|
|
163
|
+
/\[[^\]]*]\([^)]+\)/,
|
|
164
|
+
// Images
|
|
165
|
+
/!\[[^\]]*]\([^)]+\)/,
|
|
166
|
+
// Lists
|
|
167
|
+
/^[*+-]\s+/m, /^\d+\.\s+/m,
|
|
168
|
+
// Blockquotes
|
|
169
|
+
/^>\s+/m,
|
|
170
|
+
// Tables
|
|
171
|
+
/\|.*\|.*\|/,
|
|
172
|
+
// Horizontal rules
|
|
173
|
+
/^---+$/m, /^\*\*\*+$/m,
|
|
174
|
+
// Strikethrough
|
|
175
|
+
/~~[^~]+~~/];
|
|
176
|
+
return markdownPatterns.some(function (pattern) {
|
|
177
|
+
return pattern.test(text);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Get specific markdown patterns found in text
|
|
183
|
+
*/
|
|
184
|
+
}, {
|
|
185
|
+
key: "getMarkdownPatterns",
|
|
186
|
+
value: function getMarkdownPatterns(text) {
|
|
187
|
+
var patterns = [{
|
|
188
|
+
name: 'headers',
|
|
189
|
+
regex: /^#{1,6}\s+/m
|
|
190
|
+
}, {
|
|
191
|
+
name: 'bold',
|
|
192
|
+
regex: /\*{2}[^*]+\*{2}/
|
|
193
|
+
}, {
|
|
194
|
+
name: 'italic',
|
|
195
|
+
regex: /\*[^*]+\*/
|
|
196
|
+
}, {
|
|
197
|
+
name: 'code-blocks',
|
|
198
|
+
regex: /```[\S\s]*```/
|
|
199
|
+
}, {
|
|
200
|
+
name: 'inline-code',
|
|
201
|
+
regex: /`[^`]+`/
|
|
202
|
+
}, {
|
|
203
|
+
name: 'links',
|
|
204
|
+
regex: /\[[^\]]*]\([^)]+\)/
|
|
205
|
+
}, {
|
|
206
|
+
name: 'images',
|
|
207
|
+
regex: /!\[[^\]]*]\([^)]+\)/
|
|
208
|
+
}, {
|
|
209
|
+
name: 'lists',
|
|
210
|
+
regex: /^[*+-]\s+/m
|
|
211
|
+
}, {
|
|
212
|
+
name: 'ordered-lists',
|
|
213
|
+
regex: /^\d+\.\s+/m
|
|
214
|
+
}, {
|
|
215
|
+
name: 'blockquotes',
|
|
216
|
+
regex: /^>\s+/m
|
|
217
|
+
}, {
|
|
218
|
+
name: 'tables',
|
|
219
|
+
regex: /\|.*\|.*\|/
|
|
220
|
+
}, {
|
|
221
|
+
name: 'horizontal-rules',
|
|
222
|
+
regex: /^---+$/m
|
|
223
|
+
}, {
|
|
224
|
+
name: 'strikethrough',
|
|
225
|
+
regex: /~~[^~]+~~/
|
|
226
|
+
}];
|
|
227
|
+
return patterns.filter(function (_ref2) {
|
|
228
|
+
var regex = _ref2.regex;
|
|
229
|
+
return regex.test(text);
|
|
230
|
+
}).map(function (_ref3) {
|
|
231
|
+
var name = _ref3.name;
|
|
232
|
+
return name;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Handle markdown paste by parsing and inserting as structured content
|
|
238
|
+
*/
|
|
239
|
+
}, {
|
|
240
|
+
key: "handleMarkdownPaste",
|
|
241
|
+
value: function handleMarkdownPaste(editor, text) {
|
|
242
|
+
try {
|
|
243
|
+
// Use the markdown data source to parse the content
|
|
244
|
+
var root = parseMarkdownToLexical(text, this.service.markdownReaders);
|
|
245
|
+
var selection = $getSelection();
|
|
246
|
+
var nodes = $generateNodesFromSerializedNodes(root.children);
|
|
247
|
+
$insertGeneratedNodes(editor, nodes, selection);
|
|
248
|
+
return true;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error('Failed to handle markdown paste:', error);
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
120
253
|
}
|
|
121
254
|
}]);
|
|
122
255
|
return MarkdownPlugin;
|
|
@@ -1,60 +1,7 @@
|
|
|
1
|
-
import { ElementNode, LexicalNode,
|
|
2
|
-
import { IServiceID } from "../../../types/kernel";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
intraword?: boolean;
|
|
6
|
-
process?: (selection: RangeSelection) => void;
|
|
7
|
-
tag: string;
|
|
8
|
-
type: 'text-format';
|
|
9
|
-
}>;
|
|
10
|
-
export type TextMatchTransformer = Readonly<{
|
|
11
|
-
/**
|
|
12
|
-
* For import operations, this function can be used to determine the end index of the match, after `importRegExp` has matched.
|
|
13
|
-
* Without this function, the end index will be determined by the length of the match from `importRegExp`. Manually determining the end index can be useful if
|
|
14
|
-
* the match from `importRegExp` is not the entire text content of the node. That way, `importRegExp` can be used to match only the start of the node, and `getEndIndex`
|
|
15
|
-
* can be used to match the end of the node.
|
|
16
|
-
*
|
|
17
|
-
* @returns The end index of the match, or false if the match was unsuccessful and a different transformer should be tried.
|
|
18
|
-
*/
|
|
19
|
-
getEndIndex?: (node: TextNode, match: RegExpMatchArray) => number | false;
|
|
20
|
-
/**
|
|
21
|
-
* This regex determines what text is matched during markdown imports
|
|
22
|
-
*/
|
|
23
|
-
importRegExp?: RegExp;
|
|
24
|
-
/**
|
|
25
|
-
* This regex determines what text is matched for markdown shortcuts while typing in the editor
|
|
26
|
-
*/
|
|
27
|
-
regExp: RegExp;
|
|
28
|
-
/**
|
|
29
|
-
* Determines how the matched markdown text should be transformed into a node during the markdown import process
|
|
30
|
-
*
|
|
31
|
-
* @returns nothing, or a TextNode that may be a child of the new node that is created.
|
|
32
|
-
* If a TextNode is returned, text format matching will be applied to it (e.g. bold, italic, etc.)
|
|
33
|
-
*/
|
|
34
|
-
replace?: (node: TextNode, match: RegExpMatchArray) => void | TextNode;
|
|
35
|
-
/**
|
|
36
|
-
* Single character that allows the transformer to trigger when typed in the editor. This does not affect markdown imports outside of the markdown shortcut plugin.
|
|
37
|
-
* If the trigger is matched, the `regExp` will be used to match the text in the second step.
|
|
38
|
-
*/
|
|
39
|
-
trigger?: string;
|
|
40
|
-
type: 'text-match';
|
|
41
|
-
}>;
|
|
42
|
-
export type ElementTransformer = {
|
|
43
|
-
regExp: RegExp;
|
|
44
|
-
/**
|
|
45
|
-
* `replace` is called when markdown is imported or typed in the editor
|
|
46
|
-
*
|
|
47
|
-
* @return return false to cancel the transform, even though the regex matched. Lexical will then search for the next transformer.
|
|
48
|
-
*/
|
|
49
|
-
replace: (parentNode: ElementNode, children: Array<LexicalNode>, match: Array<string>,
|
|
50
|
-
/**
|
|
51
|
-
* Whether the match is from an import operation (e.g. through `$convertFromMarkdownString`) or not (e.g. through typing in the editor).
|
|
52
|
-
*/
|
|
53
|
-
isImport: boolean) => boolean | void;
|
|
54
|
-
trigger?: 'enter';
|
|
55
|
-
type: 'element';
|
|
56
|
-
};
|
|
57
|
-
export type Transformer = ElementTransformer | TextFormatTransformer | TextMatchTransformer;
|
|
1
|
+
import { ElementNode, LexicalNode, TextNode } from 'lexical';
|
|
2
|
+
import type { IEditorKernel, IServiceID } from "../../../types/kernel";
|
|
3
|
+
import type { MarkdownReaderFunc, TransformerRecord, TransfromerRecordArray } from '../data-source/markdown/parse';
|
|
4
|
+
import type { Transformer } from './transformers';
|
|
58
5
|
export interface IMarkdownWriterContext {
|
|
59
6
|
/**
|
|
60
7
|
* Add processor
|
|
@@ -75,7 +22,15 @@ export interface IMarkdownWriterContext {
|
|
|
75
22
|
*/
|
|
76
23
|
wrap: (before: string, after: string) => void;
|
|
77
24
|
}
|
|
25
|
+
export declare const MARKDOWN_WRITER_LEVEL_MAX = 0;
|
|
26
|
+
export declare const MARKDOWN_READER_LEVEL_HIGH = 1;
|
|
27
|
+
export declare const MARKDOWN_READER_LEVEL_NORMAL = 2;
|
|
28
|
+
export type MARKDOWN_READER_LEVEL = typeof MARKDOWN_READER_LEVEL_HIGH | typeof MARKDOWN_READER_LEVEL_NORMAL | typeof MARKDOWN_WRITER_LEVEL_MAX;
|
|
78
29
|
export interface IMarkdownShortCutService {
|
|
30
|
+
/**
|
|
31
|
+
* Register Markdown reader
|
|
32
|
+
*/
|
|
33
|
+
registerMarkdownReader<K extends keyof TransformerRecord>(type: K, reader: MarkdownReaderFunc<K>, level?: MARKDOWN_READER_LEVEL): void;
|
|
79
34
|
registerMarkdownShortCut(transformer: Transformer): void;
|
|
80
35
|
registerMarkdownShortCuts(transformers: Transformer[]): void;
|
|
81
36
|
/**
|
|
@@ -93,46 +48,24 @@ export declare class MarkdownShortCutService implements IMarkdownShortCutService
|
|
|
93
48
|
private textMatchTransformers;
|
|
94
49
|
private logger;
|
|
95
50
|
private _markdownWriters;
|
|
96
|
-
|
|
97
|
-
|
|
51
|
+
private _markdownReaders;
|
|
52
|
+
constructor(kernel?: IEditorKernel | undefined);
|
|
53
|
+
get markdownWriters(): Record<string, (_ctx: IMarkdownWriterContext, _node: LexicalNode) => boolean | void>;
|
|
54
|
+
get markdownReaders(): TransfromerRecordArray;
|
|
98
55
|
private _textFormatTransformersByTrigger;
|
|
99
56
|
private _textMatchTransformersByTrigger;
|
|
100
57
|
get textMatchTransformersByTrigger(): Readonly<Record<string, Readonly<{
|
|
101
|
-
/**
|
|
102
|
-
* For import operations, this function can be used to determine the end index of the match, after `importRegExp` has matched.
|
|
103
|
-
* Without this function, the end index will be determined by the length of the match from `importRegExp`. Manually determining the end index can be useful if
|
|
104
|
-
* the match from `importRegExp` is not the entire text content of the node. That way, `importRegExp` can be used to match only the start of the node, and `getEndIndex`
|
|
105
|
-
* can be used to match the end of the node.
|
|
106
|
-
*
|
|
107
|
-
* @returns The end index of the match, or false if the match was unsuccessful and a different transformer should be tried.
|
|
108
|
-
*/
|
|
109
58
|
getEndIndex?: ((node: TextNode, match: RegExpMatchArray) => number | false) | undefined;
|
|
110
|
-
/**
|
|
111
|
-
* This regex determines what text is matched during markdown imports
|
|
112
|
-
*/
|
|
113
59
|
importRegExp?: RegExp | undefined;
|
|
114
|
-
/**
|
|
115
|
-
* This regex determines what text is matched for markdown shortcuts while typing in the editor
|
|
116
|
-
*/
|
|
117
60
|
regExp: RegExp;
|
|
118
|
-
/**
|
|
119
|
-
* Determines how the matched markdown text should be transformed into a node during the markdown import process
|
|
120
|
-
*
|
|
121
|
-
* @returns nothing, or a TextNode that may be a child of the new node that is created.
|
|
122
|
-
* If a TextNode is returned, text format matching will be applied to it (e.g. bold, italic, etc.)
|
|
123
|
-
*/
|
|
124
61
|
replace?: ((node: TextNode, match: RegExpMatchArray) => void | TextNode) | undefined;
|
|
125
|
-
/**
|
|
126
|
-
* Single character that allows the transformer to trigger when typed in the editor. This does not affect markdown imports outside of the markdown shortcut plugin.
|
|
127
|
-
* If the trigger is matched, the `regExp` will be used to match the text in the second step.
|
|
128
|
-
*/
|
|
129
62
|
trigger?: string | undefined;
|
|
130
63
|
type: "text-match";
|
|
131
64
|
}>[]>>;
|
|
132
65
|
get textFormatTransformersByTrigger(): Readonly<Record<string, readonly Readonly<{
|
|
133
|
-
format?: readonly TextFormatType[] | undefined;
|
|
66
|
+
format?: readonly import("lexical").TextFormatType[] | undefined;
|
|
134
67
|
intraword?: boolean | undefined;
|
|
135
|
-
process?: ((selection: RangeSelection) => void) | undefined;
|
|
68
|
+
process?: ((selection: import("lexical").RangeSelection) => void) | undefined;
|
|
136
69
|
tag: string;
|
|
137
70
|
type: "text-format";
|
|
138
71
|
}>[]>>;
|
|
@@ -141,4 +74,5 @@ export declare class MarkdownShortCutService implements IMarkdownShortCutService
|
|
|
141
74
|
testTransformers(parentNode: ElementNode, anchorNode: TextNode, anchorOffset: number, trigger?: 'enter'): boolean;
|
|
142
75
|
runTransformers(parentNode: ElementNode, anchorNode: TextNode, anchorOffset: number, trigger?: 'enter'): boolean;
|
|
143
76
|
registerMarkdownWriter(type: string, writer: (ctx: IMarkdownWriterContext, node: LexicalNode) => boolean | void): void;
|
|
77
|
+
registerMarkdownReader<K extends keyof TransformerRecord>(type: K, reader: MarkdownReaderFunc<K>, level?: MARKDOWN_READER_LEVEL): void;
|
|
144
78
|
}
|