@lobehub/editor 1.19.0 → 1.20.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.
Files changed (52) hide show
  1. package/es/index.d.ts +2 -0
  2. package/es/index.js +4 -0
  3. package/es/plugins/code/node/code.d.ts +1 -1
  4. package/es/plugins/code/node/code.js +1 -1
  5. package/es/plugins/code/react/CodeReactPlugin.js +1 -1
  6. package/es/plugins/codeblock/plugin/index.js +1 -1
  7. package/es/plugins/codeblock/react/ReactCodeblockPlugin.js +1 -1
  8. package/es/plugins/common/plugin/index.js +2 -1
  9. package/es/plugins/common/plugin/mdReader.d.ts +1 -1
  10. package/es/plugins/common/react/ReactPlainText.js +1 -1
  11. package/es/plugins/file/plugin/index.js +1 -1
  12. package/es/plugins/hr/plugin/index.js +1 -1
  13. package/es/plugins/hr/react/ReactHRPlugin.js +1 -1
  14. package/es/plugins/link/plugin/index.js +1 -1
  15. package/es/plugins/link/react/ReactLinkPlugin.js +1 -1
  16. package/es/plugins/link-highlight/command/index.d.ts +3 -0
  17. package/es/plugins/link-highlight/command/index.js +46 -0
  18. package/es/plugins/link-highlight/index.d.ts +4 -0
  19. package/es/plugins/link-highlight/index.js +5 -0
  20. package/es/plugins/link-highlight/node/link-highlight.d.ts +29 -0
  21. package/es/plugins/link-highlight/node/link-highlight.js +223 -0
  22. package/es/plugins/link-highlight/plugin/index.d.ts +15 -0
  23. package/es/plugins/link-highlight/plugin/index.js +187 -0
  24. package/es/plugins/link-highlight/plugin/registry.d.ts +6 -0
  25. package/es/plugins/link-highlight/plugin/registry.js +61 -0
  26. package/es/plugins/link-highlight/react/ReactLinkHighlightPlugin.d.ts +4 -0
  27. package/es/plugins/link-highlight/react/ReactLinkHighlightPlugin.js +35 -0
  28. package/es/plugins/link-highlight/react/index.d.ts +1 -0
  29. package/es/plugins/link-highlight/react/index.js +1 -0
  30. package/es/plugins/link-highlight/react/style.d.ts +3 -0
  31. package/es/plugins/link-highlight/react/style.js +10 -0
  32. package/es/plugins/link-highlight/react/type.d.ts +13 -0
  33. package/es/plugins/link-highlight/react/type.js +1 -0
  34. package/es/plugins/link-highlight/utils/index.d.ts +17 -0
  35. package/es/plugins/link-highlight/utils/index.js +43 -0
  36. package/es/plugins/list/plugin/index.js +1 -1
  37. package/es/plugins/list/react/ReactListPlugin.js +1 -1
  38. package/es/plugins/markdown/index.d.ts +1 -0
  39. package/es/plugins/markdown/index.js +2 -1
  40. package/es/plugins/markdown/plugin/index.js +57 -3
  41. package/es/plugins/markdown/utils/url-validator.d.ts +4 -0
  42. package/es/plugins/markdown/utils/url-validator.js +6 -0
  43. package/es/plugins/math/plugin/index.js +1 -1
  44. package/es/plugins/math/react/index.js +1 -1
  45. package/es/plugins/mention/plugin/index.js +1 -1
  46. package/es/plugins/mention/react/ReactMentionPlugin.js +1 -1
  47. package/es/plugins/table/plugin/index.js +1 -1
  48. package/es/react/Editor/Editor.js +1 -1
  49. package/es/react/hooks/useEditorState/index.js +46 -6
  50. package/es/utils/url.d.ts +15 -0
  51. package/es/utils/url.js +51 -0
  52. package/package.json +1 -1
@@ -0,0 +1,187 @@
1
+ var _class;
2
+ 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); }
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
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
10
+ 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); } }
11
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
12
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
13
+ function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
14
+ function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
15
+ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
16
+ function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
17
+ function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
18
+ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
19
+ 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; }
20
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
21
+ 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); }
22
+ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_NORMAL, PASTE_COMMAND } from 'lexical';
23
+ import { INodeHelper } from "../../../editor-kernel/inode/helper";
24
+ import { KernelPlugin } from "../../../editor-kernel/plugin";
25
+ import { $createCursorNode, cursorNodeSerialized } from "../../common/node/cursor";
26
+ import { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH } from "../../markdown/service/shortcut";
27
+ import { createDebugLogger } from "../../../utils/debug";
28
+ import { registerLinkHighlightCommand } from "../command";
29
+ import { $createLinkHighlightNode, LinkHighlightNode } from "../node/link-highlight";
30
+ import { isValidUrl } from "../utils";
31
+ import { registerLinkHighlight } from "./registry";
32
+ export var LinkHighlightPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
33
+ _inherits(LinkHighlightPlugin, _KernelPlugin);
34
+ var _super = _createSuper(LinkHighlightPlugin);
35
+ function LinkHighlightPlugin(kernel, config) {
36
+ var _this;
37
+ _classCallCheck(this, LinkHighlightPlugin);
38
+ _this = _super.call(this);
39
+ _defineProperty(_assertThisInitialized(_this), "logger", createDebugLogger('plugin', 'link-highlight'));
40
+ _defineProperty(_assertThisInitialized(_this), "urlRegex", void 0);
41
+ _this.kernel = kernel;
42
+ _this.config = config;
43
+ kernel.registerNodes([LinkHighlightNode]);
44
+ kernel.registerThemes({
45
+ linkHighlight: (config === null || config === void 0 ? void 0 : config.theme) || 'editor-link-highlight'
46
+ });
47
+
48
+ // Default URL regex that matches http(s), mailto, tel, etc.
49
+ _this.urlRegex = (config === null || config === void 0 ? void 0 : config.urlRegex) || /^(?:https?:\/\/|mailto:|tel:)[^\s"<>[\\\]^`{|}]+|^www\.[^\s"<>[\\\]^`{|}]+/i;
50
+ _this.logger.debug('LinkHighlightPlugin initialized');
51
+ return _this;
52
+ }
53
+ _createClass(LinkHighlightPlugin, [{
54
+ key: "onInit",
55
+ value: function onInit(editor) {
56
+ var _this$config,
57
+ _this$config2,
58
+ _this2 = this;
59
+ this.register(registerLinkHighlightCommand(editor));
60
+ this.register(registerLinkHighlight(editor, this.kernel, {
61
+ enableHotkey: (_this$config = this.config) === null || _this$config === void 0 ? void 0 : _this$config.enableHotkey
62
+ }));
63
+
64
+ // Register paste handler for auto-highlighting URLs
65
+ if (((_this$config2 = this.config) === null || _this$config2 === void 0 ? void 0 : _this$config2.enablePasteAutoHighlight) !== false) {
66
+ this.register(editor.registerCommand(PASTE_COMMAND, function (payload) {
67
+ var clipboardData = payload.clipboardData;
68
+ if (clipboardData && clipboardData.types && clipboardData.types.length === 1 && clipboardData.types[0] === 'text/plain') {
69
+ var data = clipboardData.getData('text/plain').trim();
70
+ // Check if the pasted content is a valid URL
71
+ if (_this2.urlRegex.test(data) && isValidUrl(data)) {
72
+ payload.stopImmediatePropagation();
73
+ payload.preventDefault();
74
+ _this2.logger.debug('Auto-highlighting pasted URL:', data);
75
+
76
+ // Insert LinkHighlightNode directly
77
+ editor.update(function () {
78
+ var selection = $getSelection();
79
+ if ($isRangeSelection(selection)) {
80
+ var linkHighlightNode = $createLinkHighlightNode(data);
81
+ var cursorNode = $createCursorNode();
82
+ selection.insertNodes([linkHighlightNode, cursorNode]);
83
+ }
84
+ });
85
+ return true;
86
+ }
87
+ }
88
+ return false;
89
+ }, COMMAND_PRIORITY_NORMAL));
90
+ }
91
+ var markdownService = this.kernel.requireService(IMarkdownShortCutService);
92
+ if (!markdownService) {
93
+ return;
94
+ }
95
+
96
+ // Register markdown writer for <link> format
97
+ markdownService.registerMarkdownWriter(LinkHighlightNode.getType(), function (ctx, node) {
98
+ ctx.appendLine("<".concat(node.getTextContent(), ">"));
99
+ return true;
100
+ });
101
+
102
+ // Register markdown shortcut for auto-link syntax: <url>
103
+ // Matches URLs wrapped in angle brackets like <https://example.com>
104
+ markdownService.registerMarkdownShortCut({
105
+ regExp: /<((?:https?:\/\/|mailto:|tel:|www\.)[^\s"<>[\\\]^`{|}]+)>\s?$/,
106
+ replace: function replace(textNode, match) {
107
+ var _match = _slicedToArray(match, 2),
108
+ url = _match[1];
109
+ if (!url || !isValidUrl(url)) {
110
+ return;
111
+ }
112
+ _this2.logger.debug('Converting markdown auto-link to LinkHighlightNode:', url);
113
+ var linkHighlightNode = $createLinkHighlightNode(url);
114
+ var cursorNode = $createCursorNode();
115
+ textNode.replace(linkHighlightNode);
116
+ linkHighlightNode.insertAfter(cursorNode);
117
+ return undefined;
118
+ },
119
+ trigger: '>',
120
+ type: 'text-match'
121
+ });
122
+
123
+ // Register HTML reader to handle <url> syntax that might be parsed as HTML
124
+ markdownService.registerMarkdownReader('html', function (node) {
125
+ var htmlValue = node.value || '';
126
+
127
+ // Check if this looks like an auto-link: <url>
128
+ var match = htmlValue.match(/^<((?:https?:\/\/|mailto:|tel:|www\.)[^\s"<>[\\\]^`{|}]+)>$/);
129
+ if (match) {
130
+ var url = match[1].replaceAll(/[\u200B-\u200D\u2060\uFEFF]/g, '');
131
+ _this2.logger.debug('Converting HTML auto-link to LinkHighlightNode:', url);
132
+ return [INodeHelper.createElementNode('linkHighlight', {
133
+ children: [cursorNodeSerialized, INodeHelper.createTextNode(url, {})],
134
+ direction: 'ltr',
135
+ format: '',
136
+ indent: 0,
137
+ type: 'linkHighlight',
138
+ version: 1
139
+ }), cursorNodeSerialized];
140
+ }
141
+
142
+ // Not an auto-link, let other handlers process it
143
+ return false;
144
+ }, MARKDOWN_READER_LEVEL_HIGH);
145
+
146
+ // Register markdown reader for 'link' type (auto-links like <url>)
147
+ // Use HIGH priority to handle auto-links before standard Link plugin
148
+ markdownService.registerMarkdownReader('link', function (node, children) {
149
+ // Check if this is an auto-link (URL and text are the same)
150
+ var url = node.url || '';
151
+
152
+ // Check if we have text children
153
+ if (!children || children.length === 0) {
154
+ return false;
155
+ }
156
+
157
+ // Get text content from children
158
+ var textContent = children.filter(function (child) {
159
+ return child.type === 'text';
160
+ }).map(function (child) {
161
+ return child.text || '';
162
+ }).join('');
163
+
164
+ // If text matches URL exactly (auto-link syntax), convert to LinkHighlightNode
165
+ if (textContent === url) {
166
+ _this2.logger.debug('Converting markdown auto-link to LinkHighlightNode:', url);
167
+ // Return array with LinkHighlightNode and trailing cursor
168
+ // Structure matches CodeNode: [node with internal cursor + text, external cursor]
169
+ return [INodeHelper.createElementNode('linkHighlight', {
170
+ children: [cursorNodeSerialized, INodeHelper.createTextNode(url, {})],
171
+ direction: 'ltr',
172
+ format: '',
173
+ indent: 0,
174
+ type: 'linkHighlight',
175
+ version: 1
176
+ }), cursorNodeSerialized];
177
+ }
178
+
179
+ // Otherwise, let standard Link plugin handle it
180
+ return false;
181
+ }, MARKDOWN_READER_LEVEL_HIGH // High priority to intercept auto-links
182
+ );
183
+ this.logger.debug('LinkHighlightPlugin initialized with markdown support');
184
+ }
185
+ }]);
186
+ return LinkHighlightPlugin;
187
+ }(KernelPlugin), _defineProperty(_class, "pluginName", 'LinkHighlightPlugin'), _class);
@@ -0,0 +1,6 @@
1
+ import { LexicalEditor } from 'lexical';
2
+ import { IEditorKernel } from "../../../types";
3
+ export interface LinkHighlightRegistryOptions {
4
+ enableHotkey?: boolean;
5
+ }
6
+ export declare function registerLinkHighlight(editor: LexicalEditor, kernel: IEditorKernel, options?: LinkHighlightRegistryOptions): () => void;
@@ -0,0 +1,61 @@
1
+ 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; } } }; }
2
+ 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); }
3
+ 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; }
4
+ import { mergeRegister } from '@lexical/utils';
5
+ import { $getNodeByKey } from 'lexical';
6
+ import { $createCursorNode } from "../../common/node/cursor";
7
+ import { HotkeyEnum } from "../../../types/hotkey";
8
+ import { INSERT_LINK_HIGHLIGHT_COMMAND } from "../command";
9
+ import { LinkHighlightNode } from "../node/link-highlight";
10
+ export function registerLinkHighlight(editor, kernel, options) {
11
+ var _ref = options || {},
12
+ _ref$enableHotkey = _ref.enableHotkey,
13
+ enableHotkey = _ref$enableHotkey === void 0 ? true : _ref$enableHotkey;
14
+ return mergeRegister(
15
+ // Update listener to ensure cursor node before first LinkHighlightNode
16
+ editor.registerUpdateListener(function (_ref2) {
17
+ var mutatedNodes = _ref2.mutatedNodes;
18
+ var linkHighlightChanged = mutatedNodes === null || mutatedNodes === void 0 ? void 0 : mutatedNodes.get(LinkHighlightNode);
19
+ var keys = (linkHighlightChanged === null || linkHighlightChanged === void 0 ? void 0 : linkHighlightChanged.keys()) || [];
20
+ var needAddBefore = new Set();
21
+ editor.read(function () {
22
+ var _iterator = _createForOfIteratorHelper(keys),
23
+ _step;
24
+ try {
25
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
26
+ var key = _step.value;
27
+ var node = $getNodeByKey(key);
28
+ if (!node) {
29
+ return;
30
+ }
31
+ var parent = node.getParent();
32
+ if ((parent === null || parent === void 0 ? void 0 : parent.getFirstChild()) === node) {
33
+ needAddBefore.add(node);
34
+ }
35
+ }
36
+ } catch (err) {
37
+ _iterator.e(err);
38
+ } finally {
39
+ _iterator.f();
40
+ }
41
+ });
42
+ if (needAddBefore.size > 0) {
43
+ editor.update(function () {
44
+ needAddBefore.forEach(function (node) {
45
+ var prev = node.getPreviousSibling();
46
+ if (!prev) {
47
+ node.insertBefore($createCursorNode());
48
+ }
49
+ });
50
+ });
51
+ }
52
+ }),
53
+ // Hotkey for toggling link highlight (Ctrl+K or Cmd+K)
54
+ kernel.registerHotkey(HotkeyEnum.Link, function () {
55
+ return editor.dispatchCommand(INSERT_LINK_HIGHLIGHT_COMMAND, undefined);
56
+ }, {
57
+ enabled: enableHotkey,
58
+ preventDefault: true,
59
+ stopPropagation: true
60
+ }));
61
+ }
@@ -0,0 +1,4 @@
1
+ import { type FC } from 'react';
2
+ import { ReactLinkHighlightPluginProps } from './type';
3
+ declare const ReactLinkHighlightPlugin: FC<ReactLinkHighlightPluginProps>;
4
+ export default ReactLinkHighlightPlugin;
@@ -0,0 +1,35 @@
1
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
+ 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."); }
3
+ 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); }
4
+ 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; }
5
+ 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; } }
6
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
+ import { useLayoutEffect } from 'react';
8
+ import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context";
9
+ import { MarkdownPlugin } from "../../markdown/plugin";
10
+ import { LinkHighlightPlugin } from "../plugin";
11
+ import { useStyles } from "./style";
12
+ var ReactLinkHighlightPlugin = function ReactLinkHighlightPlugin(_ref) {
13
+ var className = _ref.className,
14
+ _ref$enableHotkey = _ref.enableHotkey,
15
+ enableHotkey = _ref$enableHotkey === void 0 ? true : _ref$enableHotkey,
16
+ _ref$enablePasteAutoH = _ref.enablePasteAutoHighlight,
17
+ enablePasteAutoHighlight = _ref$enablePasteAutoH === void 0 ? true : _ref$enablePasteAutoH;
18
+ var _useLexicalComposerCo = useLexicalComposerContext(),
19
+ _useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
20
+ editor = _useLexicalComposerCo2[0];
21
+ var _useStyles = useStyles(),
22
+ cx = _useStyles.cx,
23
+ styles = _useStyles.styles;
24
+ useLayoutEffect(function () {
25
+ editor.registerPlugin(MarkdownPlugin);
26
+ editor.registerPlugin(LinkHighlightPlugin, {
27
+ enableHotkey: enableHotkey,
28
+ enablePasteAutoHighlight: enablePasteAutoHighlight,
29
+ theme: cx(styles.linkHighlight, className)
30
+ });
31
+ }, [className, cx, enableHotkey, enablePasteAutoHighlight, editor, styles.linkHighlight]);
32
+ return null;
33
+ };
34
+ ReactLinkHighlightPlugin.displayName = 'ReactLinkHighlightPlugin';
35
+ export default ReactLinkHighlightPlugin;
@@ -0,0 +1 @@
1
+ export { default as ReactLinkHighlightPlugin } from './ReactLinkHighlightPlugin';
@@ -0,0 +1 @@
1
+ export { default as ReactLinkHighlightPlugin } from "./ReactLinkHighlightPlugin";
@@ -0,0 +1,3 @@
1
+ export declare const useStyles: (props?: unknown) => import("antd-style").ReturnStyles<{
2
+ linkHighlight: import("antd-style").SerializedStyles;
3
+ }>;
@@ -0,0 +1,10 @@
1
+ var _templateObject;
2
+ function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
3
+ import { createStyles } from 'antd-style';
4
+ export var useStyles = createStyles(function (_ref) {
5
+ var css = _ref.css,
6
+ token = _ref.token;
7
+ return {
8
+ linkHighlight: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n cursor: pointer;\n\n display: inline;\n\n padding: 2px;\n\n color: ", ";\n text-decoration: none;\n\n transition: all 0.2s ease;\n\n &:hover {\n color: ", ";\n text-decoration: underline;\n }\n\n ne-content {\n display: inline;\n }\n "])), token.colorLink, token.colorLinkHover)
9
+ };
10
+ });
@@ -0,0 +1,13 @@
1
+ export interface ReactLinkHighlightPluginProps {
2
+ className?: string;
3
+ /**
4
+ * Enable keyboard shortcut (Ctrl+K / Cmd+K)
5
+ * @default true
6
+ */
7
+ enableHotkey?: boolean;
8
+ /**
9
+ * Enable auto-highlight when pasting URLs
10
+ * @default true
11
+ */
12
+ enablePasteAutoHighlight?: boolean;
13
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ export { isValidUrl } from "../../../utils/url";
2
+ /**
3
+ * Sanitizes a URL to ensure it's safe to use
4
+ * @param url - The URL to sanitize
5
+ * @returns The sanitized URL
6
+ */
7
+ export declare function sanitizeUrl(url: string): string;
8
+ /**
9
+ * Extracts URL from text
10
+ * @param text - The text to extract URL from
11
+ * @returns The extracted URL information or null
12
+ */
13
+ export declare function extractUrlFromText(text: string): {
14
+ index: number;
15
+ length: number;
16
+ url: string;
17
+ } | null;
@@ -0,0 +1,43 @@
1
+ // Re-export shared URL validation from utils
2
+ export { isValidUrl } from "../../../utils/url";
3
+
4
+ // URL validation regex that matches common URL patterns for extraction
5
+ var urlRegExp = new RegExp(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\w$&+,:;=-]+@)?[\d.A-Za-z-]+|(?:www.|[\w$&+,:;=-]+@)[\d.A-Za-z-]+)((?:\/[%+./~\w-_]*)?\??[\w%&+.;=@-]*#?\w*)?)/);
6
+
7
+ /**
8
+ * Sanitizes a URL to ensure it's safe to use
9
+ * @param url - The URL to sanitize
10
+ * @returns The sanitized URL
11
+ */
12
+ export function sanitizeUrl(url) {
13
+ var SUPPORTED_URL_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'sms:', 'tel:']);
14
+ try {
15
+ var parsedUrl = new URL(url);
16
+ if (!SUPPORTED_URL_PROTOCOLS.has(parsedUrl.protocol)) {
17
+ return 'about:blank';
18
+ }
19
+ } catch (_unused) {
20
+ return url;
21
+ }
22
+ return url;
23
+ }
24
+
25
+ /**
26
+ * Extracts URL from text
27
+ * @param text - The text to extract URL from
28
+ * @returns The extracted URL information or null
29
+ */
30
+ export function extractUrlFromText(text) {
31
+ var _index;
32
+ var match = urlRegExp.exec(text);
33
+ if (!match) return null;
34
+ var raw = match[0];
35
+ var start = (_index = match.index) !== null && _index !== void 0 ? _index : text.indexOf(raw);
36
+ // Trim trailing punctuation that often follows inline links
37
+ var trimmed = raw.replace(/[\),\.:;\]]+$/, '');
38
+ return {
39
+ index: start,
40
+ length: trimmed.length,
41
+ url: trimmed
42
+ };
43
+ }
@@ -19,7 +19,7 @@ import { cx } from 'antd-style';
19
19
  import { $isRootNode } from 'lexical';
20
20
  import { INodeHelper } from "../../../editor-kernel/inode/helper";
21
21
  import { KernelPlugin } from "../../../editor-kernel/plugin";
22
- import { IMarkdownShortCutService } from "../../markdown";
22
+ import { IMarkdownShortCutService } from "../../markdown/service/shortcut";
23
23
  import { listReplace } from "../utils";
24
24
  import { registerCheckList } from "./checkList";
25
25
  import { registerListCommands } from "./registry";
@@ -8,7 +8,7 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
8
8
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
9
9
  import { useLayoutEffect } from 'react';
10
10
  import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context";
11
- import { MarkdownPlugin } from "../../markdown";
11
+ import { MarkdownPlugin } from "../../markdown/plugin";
12
12
  import { ListPlugin } from "../plugin";
13
13
  import { useStyles } from "./style";
14
14
  var ReactListPlugin = function ReactListPlugin(_ref) {
@@ -5,3 +5,4 @@ export type { MARKDOWN_READER_LEVEL } from './service/shortcut';
5
5
  export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX, } from './service/shortcut';
6
6
  export { isPunctuationChar } from './utils';
7
7
  export { detectCodeLanguage, detectLanguage } from './utils/detectLanguage';
8
+ export { isPureUrl, isValidUrl } from './utils/url-validator';
@@ -3,4 +3,5 @@ export { MarkdownPlugin } from "./plugin";
3
3
  export { default as ReactMarkdownPlugin } from "./react";
4
4
  export { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH, MARKDOWN_READER_LEVEL_NORMAL, MARKDOWN_WRITER_LEVEL_MAX } from "./service/shortcut";
5
5
  export { isPunctuationChar } from "./utils";
6
- export { detectCodeLanguage, detectLanguage } from "./utils/detectLanguage";
6
+ export { detectCodeLanguage, detectLanguage } from "./utils/detectLanguage";
7
+ export { isPureUrl, isValidUrl } from "./utils/url-validator";
@@ -13,15 +13,17 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
13
13
  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; }
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
- import { $createCodeNode, $isCodeNode } from '@lexical/code';
16
+ import { $createCodeNode, $isCodeHighlightNode, $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
+ import { $isCodeInlineNode } from "../../code/node/code";
19
20
  import { createDebugLogger } from "../../../utils/debug";
20
21
  import { registerMarkdownCommand } from "../command";
21
22
  import MarkdownDataSource from "../data-source/markdown-data-source";
22
23
  import { IMarkdownShortCutService, MarkdownShortCutService } from "../service/shortcut";
23
24
  import { canContainTransformableMarkdown } from "../utils";
24
25
  import { detectCodeLanguage, detectLanguage } from "../utils/detectLanguage";
26
+ import { isValidUrl as isValidLinkUrl } from "../utils/url-validator";
25
27
  export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
26
28
  _inherits(MarkdownPlugin, _KernelPlugin);
27
29
  var _super = _createSuper(MarkdownPlugin);
@@ -125,8 +127,10 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
125
127
  var clipboardData = event.clipboardData;
126
128
  if (!clipboardData) return false;
127
129
 
128
- // Get clipboard content
129
- var text = clipboardData.getData('text/plain').trimEnd();
130
+ // Get clipboard content and clean BOM/zero-width characters
131
+ var rawText = clipboardData.getData('text/plain').trimEnd();
132
+ // Remove BOM, zero-width spaces, and other invisible characters
133
+ var text = rawText.replaceAll(/[\u200B-\u200D\u2060\uFEFF]/g, '');
130
134
  var html = clipboardData.getData('text/html').trimEnd();
131
135
 
132
136
  // If there's no text content, let Lexical handle it
@@ -137,6 +141,48 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
137
141
  htmlLength: (html === null || html === void 0 ? void 0 : html.length) || 0,
138
142
  textLength: text.length
139
143
  });
144
+
145
+ // Check if cursor is inside code block or inline code
146
+ // If so, always paste as plain text
147
+ var isInCodeBlock = editor.read(function () {
148
+ var selection = $getSelection();
149
+ if (!$isRangeSelection(selection)) return false;
150
+ var anchorNode = selection.anchor.getNode();
151
+ var focusNode = selection.focus.getNode();
152
+
153
+ // Check if in code block (CodeNode or CodeHighlightNode)
154
+ var anchorParent = anchorNode.getParent();
155
+ var focusParent = focusNode.getParent();
156
+ if ($isCodeNode(anchorNode) || $isCodeNode(focusNode)) {
157
+ return true;
158
+ }
159
+ if ($isCodeNode(anchorParent) || $isCodeNode(focusParent)) {
160
+ return true;
161
+ }
162
+ if ($isCodeHighlightNode(anchorNode) || $isCodeHighlightNode(focusNode)) {
163
+ return true;
164
+ }
165
+
166
+ // Check if in inline code
167
+ if ($isCodeInlineNode(anchorNode) || $isCodeInlineNode(focusNode)) {
168
+ return true;
169
+ }
170
+ if ($isCodeInlineNode(anchorParent) || $isCodeInlineNode(focusParent)) {
171
+ return true;
172
+ }
173
+ return false;
174
+ });
175
+ if (isInCodeBlock) {
176
+ _this2.logger.debug('cursor in code block, pasting as plain text');
177
+ event.preventDefault();
178
+ event.stopPropagation();
179
+ editor.update(function () {
180
+ var selection = $getSelection();
181
+ if (!$isRangeSelection(selection)) return;
182
+ selection.insertText(text);
183
+ });
184
+ return true;
185
+ }
140
186
  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;
141
187
 
142
188
  // If markdown formatting is disabled, we're done
@@ -163,6 +209,7 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
163
209
  }
164
210
 
165
211
  // Check if markdown paste formatting is enabled (default: true)
212
+ // Note: URL pasting is handled by Link/LinkHighlight plugins themselves
166
213
 
167
214
  // Check if content is code (JSON, SQL, etc.) and should be inserted as code block
168
215
  var codeInfo = _this2.detectCodeContent(text);
@@ -227,6 +274,13 @@ export var MarkdownPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
227
274
  return true; // Command handled
228
275
  }
229
276
 
277
+ // Check if the pasted content is a pure URL
278
+ // If so, let Link/LinkHighlight plugins handle it
279
+ if (clipboardData.types.length === 1 && clipboardData.types[0] === 'text/plain' && isValidLinkUrl(text)) {
280
+ _this2.logger.debug('pure URL detected, letting Link/LinkHighlight plugins handle');
281
+ return false; // Let other plugins handle URL paste
282
+ }
283
+
230
284
  // Force plain text paste for external content
231
285
  event.preventDefault();
232
286
  event.stopPropagation();
@@ -0,0 +1,4 @@
1
+ /**
2
+ * URL validation utilities for detecting and validating URLs in pasted content
3
+ */
4
+ export { isPureUrl, isValidUrl } from "../../../utils/url";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * URL validation utilities for detecting and validating URLs in pasted content
3
+ */
4
+
5
+ // Re-export shared URL utilities from common utils
6
+ export { isPureUrl, isValidUrl } from "../../../utils/url";
@@ -22,7 +22,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
22
22
  import { $createNodeSelection, $setSelection } from 'lexical';
23
23
  import { INodeHelper } from "../../../editor-kernel/inode/helper";
24
24
  import { KernelPlugin } from "../../../editor-kernel/plugin";
25
- import { IMarkdownShortCutService } from "../../markdown";
25
+ import { IMarkdownShortCutService } from "../../markdown/service/shortcut";
26
26
  import { createDebugLogger } from "../../../utils/debug";
27
27
  import { registerMathCommand } from "../command";
28
28
  import { $createMathBlockNode, $createMathInlineNode, MathBlockNode, MathInlineNode } from "../node";
@@ -8,7 +8,7 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
8
8
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
9
9
  import { useLayoutEffect } from 'react';
10
10
  import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context";
11
- import { MarkdownPlugin } from "../../markdown";
11
+ import { MarkdownPlugin } from "../../markdown/plugin";
12
12
  import { MathPlugin } from "../plugin";
13
13
  import MathEdit from "./components/MathEditor";
14
14
  import MathInline from "./components/MathInline";
@@ -14,7 +14,7 @@ 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 { KernelPlugin } from "../../../editor-kernel/plugin";
17
- import { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH } from "../../markdown";
17
+ import { IMarkdownShortCutService, MARKDOWN_READER_LEVEL_HIGH } from "../../markdown/service/shortcut";
18
18
  import { registerMentionCommand } from "../command";
19
19
  import { $isMentionNode, MentionNode } from "../node/MentionNode";
20
20
  import { registerMentionNodeSelectionObserver } from "./register";
@@ -8,7 +8,7 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
8
8
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
9
9
  import { useLayoutEffect } from 'react';
10
10
  import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context";
11
- import { MarkdownPlugin } from "../../markdown";
11
+ import { MarkdownPlugin } from "../../markdown/plugin";
12
12
  import { MentionPlugin } from "../plugin";
13
13
  import Mention from "./components/Mention";
14
14
  import { useStyles } from "./style";
@@ -17,7 +17,7 @@ import { $isTableNode, TableCellNode, TableRowNode, registerTableCellUnmergeTran
17
17
  import { cx } from 'antd-style';
18
18
  import { INodeHelper } from "../../../editor-kernel/inode/helper";
19
19
  import { KernelPlugin } from "../../../editor-kernel/plugin";
20
- import { IMarkdownShortCutService } from "../../markdown";
20
+ import { IMarkdownShortCutService } from "../../markdown/service/shortcut";
21
21
  import { registerTableCommand } from "../command";
22
22
  import { TableNode, patchTableNode } from "../node";
23
23
 
@@ -18,7 +18,7 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
18
18
  import { createElement, memo, useMemo } from 'react';
19
19
  import { ReactEditor } from "../../editor-kernel/react/react-editor";
20
20
  import { ReactEditorContent, ReactPlainText } from "../../plugins/common";
21
- import { ReactMarkdownPlugin } from "../../plugins/markdown";
21
+ import ReactMarkdownPlugin from "../../plugins/markdown/react";
22
22
  import { ReactMentionPlugin } from "../../plugins/mention";
23
23
  import { ReactSlashOption, ReactSlashPlugin } from "../../plugins/slash";
24
24
  import { useEditorContent } from "../EditorProvider";