@lobehub/editor 1.5.10 → 1.6.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.
Files changed (59) hide show
  1. package/es/common/sys.d.ts +2 -0
  2. package/es/common/sys.js +5 -0
  3. package/es/const/hotkey.d.ts +3 -0
  4. package/es/const/hotkey.js +58 -0
  5. package/es/editor-kernel/kernel.d.ts +3 -0
  6. package/es/editor-kernel/kernel.js +20 -1
  7. package/es/index.d.ts +2 -0
  8. package/es/index.js +4 -0
  9. package/es/plugins/code/plugin/index.d.ts +1 -0
  10. package/es/plugins/code/plugin/index.js +7 -6
  11. package/es/plugins/code/plugin/registry.d.ts +4 -1
  12. package/es/plugins/code/plugin/registry.js +15 -13
  13. package/es/plugins/code/react/CodeReactPlugin.js +5 -2
  14. package/es/plugins/code/react/type.d.ts +1 -0
  15. package/es/plugins/common/plugin/index.d.ts +1 -0
  16. package/es/plugins/common/plugin/index.js +4 -1
  17. package/es/plugins/common/plugin/register.d.ts +4 -1
  18. package/es/plugins/common/plugin/register.js +43 -14
  19. package/es/plugins/common/react/ReactPlainText.js +4 -1
  20. package/es/plugins/common/react/type.d.ts +1 -0
  21. package/es/plugins/link/node/LinkNode.js +1 -3
  22. package/es/plugins/link/plugin/index.d.ts +4 -0
  23. package/es/plugins/link/plugin/index.js +11 -1
  24. package/es/plugins/link/plugin/registry.d.ts +9 -0
  25. package/es/plugins/link/plugin/registry.js +108 -0
  26. package/es/plugins/link/react/ReactLinkPlugin.js +21 -50
  27. package/es/plugins/link/react/components/LinkEdit.js +41 -21
  28. package/es/plugins/link/react/components/LinkToolbar.js +65 -26
  29. package/es/plugins/link/react/type.d.ts +1 -0
  30. package/es/plugins/link/utils/index.d.ts +5 -0
  31. package/es/plugins/link/utils/index.js +14 -0
  32. package/es/plugins/list/plugin/index.d.ts +1 -0
  33. package/es/plugins/list/plugin/index.js +9 -76
  34. package/es/plugins/list/plugin/registry.d.ts +6 -0
  35. package/es/plugins/list/plugin/registry.js +98 -0
  36. package/es/plugins/list/react/ReactListPlugin.js +5 -2
  37. package/es/plugins/list/react/type.d.ts +1 -0
  38. package/es/plugins/math/react/component/MathEditor.js +61 -9
  39. package/es/plugins/math/react/component/MathEditorContainer.js +44 -25
  40. package/es/plugins/math/react/component/MathEditorContent.d.ts +2 -0
  41. package/es/plugins/math/react/component/MathEditorContent.js +7 -3
  42. package/es/plugins/math/react/component/MathInline.js +6 -1
  43. package/es/plugins/math/react/component/Placeholder.d.ts +3 -1
  44. package/es/plugins/math/react/component/Placeholder.js +11 -3
  45. package/es/plugins/math/react/style.js +2 -2
  46. package/es/react/ChatInputActions/components/ActionItem.js +8 -2
  47. package/es/react/ChatInputActions/components/ActionRender.js +10 -3
  48. package/es/react/ChatInputActions/type.d.ts +2 -1
  49. package/es/react/hooks/useEditorState/index.js +44 -8
  50. package/es/types/hotkey.d.ts +71 -0
  51. package/es/types/hotkey.js +71 -0
  52. package/es/types/kernel.d.ts +9 -0
  53. package/es/utils/hotkey/isHotkeyMatch.d.ts +1 -0
  54. package/es/utils/hotkey/isHotkeyMatch.js +9 -0
  55. package/es/utils/hotkey/parseHotkeys.d.ts +6 -0
  56. package/es/utils/hotkey/parseHotkeys.js +42 -0
  57. package/es/utils/hotkey/registerHotkey.d.ts +15 -0
  58. package/es/utils/hotkey/registerHotkey.js +32 -0
  59. package/package.json +2 -1
@@ -1,11 +1,5 @@
1
1
  'use client';
2
2
 
3
- 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); }
4
- 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; }
5
- 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; }
6
- 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; }
7
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
8
- 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); }
9
3
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
10
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."); }
11
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); }
@@ -14,15 +8,14 @@ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" !=
14
8
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
15
9
  import { computePosition, flip, offset, shift } from '@floating-ui/dom';
16
10
  import { mergeRegister } from '@lexical/utils';
17
- import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, COMMAND_PRIORITY_NORMAL, KEY_DOWN_COMMAND, isModifierMatch } from 'lexical';
11
+ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_NORMAL } from 'lexical';
18
12
  import { useLayoutEffect, useRef, useState } from 'react';
19
- import { CONTROL_OR_META } from "../../../common/sys";
20
13
  import { useLexicalEditor } from "../../../editor-kernel/react";
21
14
  import { useLexicalComposerContext } from "../../../editor-kernel/react/react-context";
22
15
  import { MarkdownPlugin } from "../../markdown";
23
- import { $isLinkNode, $toggleLink, HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND, TOGGLE_LINK_COMMAND } from "../node/LinkNode";
16
+ import { $isLinkNode, HOVER_LINK_COMMAND, HOVER_OUT_LINK_COMMAND } from "../node/LinkNode";
24
17
  import { LinkPlugin } from "../plugin";
25
- import { getSelectedNode, sanitizeUrl } from "../utils";
18
+ import { getSelectedNode } from "../utils";
26
19
  import LinkEdit, { EDIT_LINK_COMMAND } from "./components/LinkEdit";
27
20
  import LinkToolbar from "./components/LinkToolbar";
28
21
  import { useStyles } from "./style";
@@ -31,6 +24,8 @@ import { Fragment as _Fragment } from "react/jsx-runtime";
31
24
  import { jsxs as _jsxs } from "react/jsx-runtime";
32
25
  export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
33
26
  var theme = _ref.theme,
27
+ _ref$enableHotkey = _ref.enableHotkey,
28
+ enableHotkey = _ref$enableHotkey === void 0 ? true : _ref$enableHotkey,
34
29
  validateUrl = _ref.validateUrl,
35
30
  attributes = _ref.attributes;
36
31
  var _useLexicalComposerCo = useLexicalComposerContext(),
@@ -51,9 +46,12 @@ export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
51
46
  useLayoutEffect(function () {
52
47
  editor.registerPlugin(MarkdownPlugin);
53
48
  editor.registerPlugin(LinkPlugin, {
54
- theme: theme || styles
49
+ attributes: attributes,
50
+ enableHotkey: enableHotkey,
51
+ theme: theme || styles,
52
+ validateUrl: validateUrl
55
53
  });
56
- }, []);
54
+ }, [attributes, enableHotkey, styles, theme, validateUrl]);
57
55
  useLexicalEditor(function (editor) {
58
56
  return mergeRegister(editor.registerUpdateListener(function () {
59
57
  var selection = editor.read(function () {
@@ -61,7 +59,7 @@ export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
61
59
  });
62
60
  if (!selection) return;
63
61
  if ($isRangeSelection(selection)) {
64
- // Update links
62
+ // Update links for UI components
65
63
  editor.read(function () {
66
64
  var node = getSelectedNode(selection);
67
65
  var parent = node.getParent();
@@ -83,46 +81,12 @@ export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
83
81
  } else {
84
82
  state.current.isLink = false;
85
83
  }
86
- if (divRef.current) {
87
- divRef.current.style.left = '-9999px';
88
- divRef.current.style.top = '-9999px';
89
- }
90
- }), editor.registerCommand(TOGGLE_LINK_COMMAND, function (payload) {
91
- if (payload === null) {
92
- $toggleLink(payload);
93
- return true;
94
- } else if (typeof payload === 'string') {
95
- if (validateUrl === undefined || validateUrl(payload)) {
96
- $toggleLink(payload, attributes);
97
- return true;
98
- }
99
- return false;
100
- } else {
101
- var url = payload.url,
102
- target = payload.target,
103
- rel = payload.rel,
104
- title = payload.title;
105
- $toggleLink(url, _objectSpread(_objectSpread({}, attributes), {}, {
106
- rel: rel,
107
- target: target,
108
- title: title
109
- }));
110
- return true;
111
- }
112
- }, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_DOWN_COMMAND, function (e) {
113
- // ctrl + k / cmd + k
114
- if (isModifierMatch(e, CONTROL_OR_META) && 'KeyK' === e.code) {
115
- var isLink = state.current.isLink;
116
- e.preventDefault();
117
- e.stopPropagation();
118
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, isLink ? null : sanitizeUrl('https://'));
119
- return true;
120
- }
121
- return false;
122
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(HOVER_LINK_COMMAND, function (payload) {
84
+ }), editor.registerCommand(HOVER_LINK_COMMAND, function (payload) {
123
85
  if (!payload.event.target || divRef.current === null) {
124
86
  return false;
125
87
  }
88
+ // Cancel any pending hide timers when hovering a link again
89
+ clearTimeout(clearTimerRef.current);
126
90
  setLinkNode(payload.linkNode);
127
91
  computePosition(payload.event.target, divRef.current, {
128
92
  middleware: [offset(5), flip(), shift()],
@@ -157,6 +121,13 @@ export var ReactLinkPlugin = function ReactLinkPlugin(_ref) {
157
121
  onMouseEnter: function onMouseEnter() {
158
122
  clearTimeout(clearTimerRef.current);
159
123
  },
124
+ onMouseLeave: function onMouseLeave() {
125
+ clearTimeout(clearTimerRef.current);
126
+ if (divRef.current) {
127
+ divRef.current.style.left = '-9999px';
128
+ divRef.current.style.top = '-9999px';
129
+ }
130
+ },
160
131
  ref: divRef
161
132
  }), /*#__PURE__*/_jsx(LinkEdit, {})]
162
133
  });
@@ -42,6 +42,25 @@ var LinkEdit = function LinkEdit() {
42
42
  var _useStyles = useStyles(),
43
43
  styles = _useStyles.styles,
44
44
  theme = _useStyles.theme;
45
+
46
+ // 取消编辑,不保存更改
47
+ var handleCancel = useCallback(function () {
48
+ var lexicalEditor = editor.getLexicalEditor();
49
+ if (!lexicalEditor) return;
50
+
51
+ // 将焦点返回到编辑器
52
+ lexicalEditor.focus();
53
+
54
+ // 隐藏编辑面板
55
+ if (divRef.current) {
56
+ divRef.current.style.left = '-9999px';
57
+ divRef.current.style.top = '-9999px';
58
+ }
59
+ linkNodeRef.current = null;
60
+ setLinkUrl('');
61
+ setLinkText('');
62
+ setLinkDom(null);
63
+ }, [editor]);
45
64
  useEffect(function () {
46
65
  if (!linkDom || !divRef.current) {
47
66
  return;
@@ -59,6 +78,27 @@ var LinkEdit = function LinkEdit() {
59
78
  });
60
79
  }, [linkDom]);
61
80
 
81
+ // 点击编辑器外部时关闭面板
82
+ useEffect(function () {
83
+ var handlePointerDown = function handlePointerDown(event) {
84
+ if (!divRef.current) return;
85
+ var target = event.target;
86
+ if (!target) return;
87
+ // 点击面板内部忽略
88
+ if (divRef.current.contains(target)) return;
89
+ // 面板打开时(存在 linkDom)才触发关闭
90
+ if (linkDom) {
91
+ handleCancel();
92
+ }
93
+ };
94
+ document.addEventListener('mousedown', handlePointerDown);
95
+ document.addEventListener('touchstart', handlePointerDown);
96
+ return function () {
97
+ document.removeEventListener('mousedown', handlePointerDown);
98
+ document.removeEventListener('touchstart', handlePointerDown);
99
+ };
100
+ }, [handleCancel, linkDom]);
101
+
62
102
  // 提取提交逻辑到独立函数
63
103
  var handleSubmit = useCallback(function () {
64
104
  var lexicalEditor = editor.getLexicalEditor();
@@ -105,25 +145,6 @@ var LinkEdit = function LinkEdit() {
105
145
  setLinkText('');
106
146
  setLinkDom(null);
107
147
  }, [editor, linkNodeRef, linkInputRef, linkTextInputRef]);
108
-
109
- // 取消编辑,不保存更改
110
- var handleCancel = useCallback(function () {
111
- var lexicalEditor = editor.getLexicalEditor();
112
- if (!lexicalEditor) return;
113
-
114
- // 将焦点返回到编辑器
115
- lexicalEditor.focus();
116
-
117
- // 隐藏编辑面板
118
- if (divRef.current) {
119
- divRef.current.style.left = '-9999px';
120
- divRef.current.style.top = '-9999px';
121
- }
122
- linkNodeRef.current = null;
123
- setLinkUrl('');
124
- setLinkText('');
125
- setLinkDom(null);
126
- }, [editor]);
127
148
  var handleKeyDown = useCallback(function (event) {
128
149
  var lexicalEditor = editor.getLexicalEditor();
129
150
  if (!linkNodeRef.current || !linkInputRef.current || !linkTextInputRef.current || !lexicalEditor) {
@@ -253,8 +274,7 @@ var LinkEdit = function LinkEdit() {
253
274
  icon: LinkIcon
254
275
  }),
255
276
  ref: linkInputRef,
256
- value: linkUrl,
257
- variant: 'outlined'
277
+ value: linkUrl
258
278
  })]
259
279
  }), /*#__PURE__*/_jsx(Flexbox, {
260
280
  className: styles.linkEditFooter,
@@ -1,49 +1,84 @@
1
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
- var _excluded = ["linkNode", "editor"];
2
+ var _excluded = ["linkNode", "editor", "onMouseLeave"];
3
3
  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; }
4
4
  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; }
5
5
  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; }
6
6
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
7
7
  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); }
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; }
8
11
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
9
12
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
10
13
  import { ActionIconGroup } from '@lobehub/ui';
14
+ import { $createRangeSelection, $getNodeByKey, $getSelection, $isRangeSelection, $isTextNode, $setSelection } from 'lexical';
11
15
  import { EditIcon, ExternalLinkIcon, UnlinkIcon } from 'lucide-react';
12
- import { memo } from 'react';
16
+ import { memo, useCallback } from 'react';
13
17
  import { useTranslation } from "../../../../editor-kernel/react/useTranslation";
14
- import { TOGGLE_LINK_COMMAND } from "../../node/LinkNode";
18
+ import { $isLinkNode, TOGGLE_LINK_COMMAND } from "../../node/LinkNode";
15
19
  import { useStyles } from "../style";
16
20
  import { EDIT_LINK_COMMAND } from "./LinkEdit";
17
21
  import { jsx as _jsx } from "react/jsx-runtime";
18
22
  var LinkToolbar = /*#__PURE__*/memo(function (_ref) {
19
23
  var linkNode = _ref.linkNode,
20
24
  editor = _ref.editor,
25
+ onMouseLeave = _ref.onMouseLeave,
21
26
  rest = _objectWithoutProperties(_ref, _excluded);
22
27
  var _useStyles = useStyles(),
23
28
  styles = _useStyles.styles;
24
29
  var t = useTranslation();
25
- var handleEdit = function handleEdit() {
26
- // Edit link
27
- if (linkNode) {
28
- editor.dispatchCommand(EDIT_LINK_COMMAND, {
29
- linkNode: linkNode,
30
- linkNodeDOM: editor.getElementByKey(linkNode.getKey())
31
- });
32
- }
33
- };
34
- var handleRemove = function handleRemove() {
35
- // Remove link
36
- editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
37
- };
38
- var handleOpenLink = function handleOpenLink() {
39
- // Open link in new window
40
- if (linkNode) {
41
- var url = editor.read(function () {
42
- return linkNode.getURL();
43
- });
44
- window.open(url, '_blank');
45
- }
46
- };
30
+ var handleEdit = useCallback(function () {
31
+ if (!linkNode) return;
32
+ editor.dispatchCommand(EDIT_LINK_COMMAND, {
33
+ linkNode: linkNode,
34
+ linkNodeDOM: editor.getElementByKey(linkNode.getKey())
35
+ });
36
+ }, [editor, linkNode]);
37
+ var handleRemove = useCallback(function () {
38
+ if (!linkNode) return;
39
+ editor.update(function () {
40
+ var node = $getNodeByKey(linkNode.getKey());
41
+ if (!$isLinkNode(node)) return;
42
+
43
+ // Try to create a range selection that covers the link's text
44
+ var selection = $getSelection();
45
+ if (!selection || !$isRangeSelection(selection)) {
46
+ $setSelection($createRangeSelection());
47
+ selection = $getSelection();
48
+ }
49
+ var first = node.getFirstDescendant();
50
+ var last = node.getLastDescendant();
51
+ if (selection && $isRangeSelection(selection) && $isTextNode(first) && $isTextNode(last)) {
52
+ selection.anchor.set(first.getKey(), 0, 'text');
53
+ selection.focus.set(last.getKey(), last.getTextContentSize(), 'text');
54
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
55
+ return;
56
+ }
57
+
58
+ // Fallback: directly unwrap the link node preserving its children
59
+ var children = node.getChildren();
60
+ var _iterator = _createForOfIteratorHelper(children),
61
+ _step;
62
+ try {
63
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
64
+ var child = _step.value;
65
+ node.insertBefore(child);
66
+ }
67
+ } catch (err) {
68
+ _iterator.e(err);
69
+ } finally {
70
+ _iterator.f();
71
+ }
72
+ node.remove();
73
+ });
74
+ }, [editor, linkNode]);
75
+ var handleOpenLink = useCallback(function () {
76
+ if (!linkNode) return;
77
+ var url = editor.read(function () {
78
+ return linkNode.getURL();
79
+ });
80
+ window.open(url, '_blank');
81
+ }, [editor, linkNode]);
47
82
  return /*#__PURE__*/_jsx(ActionIconGroup, _objectSpread({
48
83
  className: styles.linkToolbar,
49
84
  items: [{
@@ -60,8 +95,12 @@ var LinkToolbar = /*#__PURE__*/memo(function (_ref) {
60
95
  icon: UnlinkIcon,
61
96
  key: 'unlink',
62
97
  label: t('link.unlink'),
63
- onClick: handleRemove
98
+ onClick: function onClick(e) {
99
+ handleRemove();
100
+ onMouseLeave === null || onMouseLeave === void 0 || onMouseLeave(e);
101
+ }
64
102
  }],
103
+ onMouseLeave: onMouseLeave,
65
104
  shadow: true,
66
105
  size: {
67
106
  blockSize: 32,
@@ -2,6 +2,7 @@ import { LinkAttributes } from "../node/LinkNode";
2
2
  export interface ReactLinkPluginProps {
3
3
  attributes?: LinkAttributes;
4
4
  className?: string;
5
+ enableHotkey?: boolean;
5
6
  theme?: {
6
7
  link?: string;
7
8
  };
@@ -1,4 +1,9 @@
1
1
  import { ElementNode, RangeSelection, TextNode } from 'lexical';
2
2
  export declare function sanitizeUrl(url: string): string;
3
3
  export declare function validateUrl(url: string): boolean;
4
+ export declare function extractUrlFromText(text: string): {
5
+ index: number;
6
+ length: number;
7
+ url: string;
8
+ } | null;
4
9
  export declare function getSelectedNode(selection: RangeSelection): TextNode | ElementNode;
@@ -20,6 +20,20 @@ export function validateUrl(url) {
20
20
  // Maybe show a dialog where they user can type the URL before inserting it.
21
21
  return url === 'https://' || urlRegExp.test(url);
22
22
  }
23
+ export function extractUrlFromText(text) {
24
+ var _index;
25
+ var match = urlRegExp.exec(text);
26
+ if (!match) return null;
27
+ var raw = match[0];
28
+ var start = (_index = match.index) !== null && _index !== void 0 ? _index : text.indexOf(raw);
29
+ // Trim trailing punctuation that often follows inline links
30
+ var trimmed = raw.replace(/[\),\.:;\]]+$/, '');
31
+ return {
32
+ index: start,
33
+ length: trimmed.length,
34
+ url: trimmed
35
+ };
36
+ }
23
37
  export function getSelectedNode(selection) {
24
38
  var anchor = selection.anchor;
25
39
  var focus = selection.focus;
@@ -1,5 +1,6 @@
1
1
  import { IEditorPluginConstructor } from "../../../types";
2
2
  export interface ListPluginOptions {
3
+ enableHotkey?: boolean;
3
4
  theme?: string;
4
5
  }
5
6
  export declare const ListPlugin: IEditorPluginConstructor<ListPluginOptions>;
@@ -13,20 +13,18 @@ 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 { $createListNode, $isListItemNode, $isListNode, ListItemNode, ListNode, registerList, registerListStrictIndentTransform } from '@lexical/list';
16
+ import { $isListItemNode, $isListNode, ListItemNode, ListNode, registerList, registerListStrictIndentTransform } from '@lexical/list';
17
17
  import { $getNearestNodeOfType } from '@lexical/utils';
18
18
  import { cx } from 'antd-style';
19
- import { $createParagraphNode, $getSelection, $isRangeSelection, $isRootNode, $isTextNode, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, INDENT_CONTENT_COMMAND, INSERT_TAB_COMMAND, KEY_BACKSPACE_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND } from 'lexical';
19
+ import { $isRootNode } from 'lexical';
20
20
  import { KernelPlugin } from "../../../editor-kernel/plugin";
21
21
  import { IMarkdownShortCutService } from "../../markdown";
22
- import { $indentOverTab, listReplace } from "../utils";
22
+ import { listReplace } from "../utils";
23
23
  import { registerCheckList } from "./checkList";
24
+ import { registerListCommands } from "./registry";
24
25
  var ORDERED_LIST_REGEX = /^(\s*)(\d+)\.\s/;
25
26
  var UNORDERED_LIST_REGEX = /^(\s*)[*+-]\s/;
26
27
  var CHECK_LIST_REGEX = /^(\s*)(?:-\s)?\s?(\[(\s|x)?])\s/i;
27
-
28
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
29
-
30
28
  export var ListPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
31
29
  _inherits(ListPlugin, _KernelPlugin);
32
30
  var _super = _createSuper(ListPlugin);
@@ -37,6 +35,7 @@ export var ListPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
37
35
  _this = _super.call(this);
38
36
  // Register the list nodes
39
37
  _this.kernel = kernel;
38
+ _this.config = config;
40
39
  kernel.registerNodes([ListNode, ListItemNode]);
41
40
  // Register themes for list nodes
42
41
  kernel.registerThemes({
@@ -121,79 +120,13 @@ export var ListPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
121
120
  _createClass(ListPlugin, [{
122
121
  key: "onInit",
123
122
  value: function onInit(editor) {
123
+ var _this$config;
124
124
  this.register(registerList(editor));
125
125
  this.register(registerCheckList(editor));
126
126
  this.register(registerListStrictIndentTransform(editor));
127
- this.registerClears(editor.registerCommand(KEY_TAB_COMMAND, function (event) {
128
- var selection = $getSelection();
129
- if (!$isRangeSelection(selection)) {
130
- return false;
131
- }
132
- event.preventDefault();
133
- var command = $indentOverTab(selection) ? event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND : INSERT_TAB_COMMAND;
134
- return editor.dispatchCommand(command, undefined);
135
- }, COMMAND_PRIORITY_EDITOR), editor.registerCommand(KEY_BACKSPACE_COMMAND, function (event) {
136
- var _listItemNode$getPare;
137
- var selection = $getSelection();
138
- if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
139
- return false;
140
- }
141
- var anchor = selection.anchor;
142
- if (anchor.offset !== 0) {
143
- return false;
144
- }
145
- var anchorNode = anchor.getNode();
146
- var listItemNode;
147
- if ($isListItemNode(anchorNode)) {
148
- listItemNode = anchorNode;
149
- } else if ($isTextNode(anchorNode)) {
150
- // Do not handle non-leading text nodes
151
- if (anchorNode.getPreviousSibling()) {
152
- return false;
153
- }
154
- var parent = anchorNode.getParentOrThrow();
155
- if (!$isListItemNode(parent)) {
156
- return false;
157
- }
158
- listItemNode = parent;
159
- }
160
- if (!listItemNode || !$isRootNode((_listItemNode$getPare = listItemNode.getParent()) === null || _listItemNode$getPare === void 0 ? void 0 : _listItemNode$getPare.getParent())) {
161
- return false;
162
- }
163
- var listNode = listItemNode.getParentOrThrow();
164
- queueMicrotask(function () {
165
- editor.update(function () {
166
- // Add null check since listItemNode might be undefined in this closure
167
- if (!listItemNode) return;
168
- var newlistNode;
169
- var isFirst = listItemNode.getPreviousSibling() === null;
170
- if (isFirst) {
171
- var _p = listItemNode.replace($createParagraphNode(), true);
172
- _p.select(0, 0);
173
- return;
174
- }
175
- var next = listItemNode.getNextSibling();
176
- if (next) {
177
- newlistNode = $createListNode(listNode.getListType(), listItemNode.getValue());
178
- }
179
- while (next && newlistNode) {
180
- next.remove();
181
- newlistNode.append(next);
182
- next = next.getNextSibling();
183
- }
184
- var p = listItemNode.replace($createParagraphNode(), true);
185
- p.remove();
186
- listNode.insertAfter(p);
187
- if (newlistNode) {
188
- p.insertAfter(newlistNode);
189
- }
190
- p.select(0, 0);
191
- });
192
- });
193
- event.stopImmediatePropagation();
194
- event.preventDefault();
195
- return true;
196
- }, COMMAND_PRIORITY_LOW));
127
+ this.register(registerListCommands(editor, this.kernel, {
128
+ enableHotkey: (_this$config = this.config) === null || _this$config === void 0 ? void 0 : _this$config.enableHotkey
129
+ }));
197
130
  }
198
131
  }]);
199
132
  return ListPlugin;
@@ -0,0 +1,6 @@
1
+ import { LexicalEditor } from 'lexical';
2
+ import { IEditorKernel } from "../../../types";
3
+ export interface ListRegistryOptions {
4
+ enableHotkey?: boolean;
5
+ }
6
+ export declare function registerListCommands(editor: LexicalEditor, kernel: IEditorKernel, options?: ListRegistryOptions): () => void;
@@ -0,0 +1,98 @@
1
+ import { $createListNode, $isListItemNode, INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list';
2
+ import { mergeRegister } from '@lexical/utils';
3
+ import { $createParagraphNode, $getSelection, $isRangeSelection, $isRootNode, $isTextNode, COMMAND_PRIORITY_EDITOR, COMMAND_PRIORITY_LOW, INDENT_CONTENT_COMMAND, INSERT_TAB_COMMAND, KEY_BACKSPACE_COMMAND, KEY_TAB_COMMAND, OUTDENT_CONTENT_COMMAND } from 'lexical';
4
+ import { HotkeyEnum } from "../../../types/hotkey";
5
+ import { $indentOverTab } from "../utils";
6
+ export function registerListCommands(editor, kernel, options) {
7
+ var _ref = options || {},
8
+ _ref$enableHotkey = _ref.enableHotkey,
9
+ enableHotkey = _ref$enableHotkey === void 0 ? true : _ref$enableHotkey;
10
+ return mergeRegister(
11
+ // Hotkey registrations
12
+ kernel.registerHotkey(HotkeyEnum.BulletList, function () {
13
+ return editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
14
+ }, {
15
+ enabled: enableHotkey,
16
+ preventDefault: true,
17
+ stopImmediatePropagation: true
18
+ }), kernel.registerHotkey(HotkeyEnum.NumberList, function () {
19
+ return editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
20
+ }, {
21
+ enabled: enableHotkey,
22
+ preventDefault: true,
23
+ stopImmediatePropagation: true
24
+ }),
25
+ // Tab key command for indentation
26
+ editor.registerCommand(KEY_TAB_COMMAND, function (event) {
27
+ var selection = $getSelection();
28
+ if (!$isRangeSelection(selection)) {
29
+ return false;
30
+ }
31
+ event.preventDefault();
32
+ var command = $indentOverTab(selection) ? event.shiftKey ? OUTDENT_CONTENT_COMMAND : INDENT_CONTENT_COMMAND : INSERT_TAB_COMMAND;
33
+ return editor.dispatchCommand(command, undefined);
34
+ }, COMMAND_PRIORITY_EDITOR),
35
+ // Backspace key command for list item handling
36
+ editor.registerCommand(KEY_BACKSPACE_COMMAND, function (event) {
37
+ var _listItemNode$getPare;
38
+ var selection = $getSelection();
39
+ if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
40
+ return false;
41
+ }
42
+ var anchor = selection.anchor;
43
+ if (anchor.offset !== 0) {
44
+ return false;
45
+ }
46
+ var anchorNode = anchor.getNode();
47
+ var listItemNode;
48
+ if ($isListItemNode(anchorNode)) {
49
+ listItemNode = anchorNode;
50
+ } else if ($isTextNode(anchorNode)) {
51
+ // Do not handle non-leading text nodes
52
+ if (anchorNode.getPreviousSibling()) {
53
+ return false;
54
+ }
55
+ var parent = anchorNode.getParentOrThrow();
56
+ if (!$isListItemNode(parent)) {
57
+ return false;
58
+ }
59
+ listItemNode = parent;
60
+ }
61
+ if (!listItemNode || !$isRootNode((_listItemNode$getPare = listItemNode.getParent()) === null || _listItemNode$getPare === void 0 ? void 0 : _listItemNode$getPare.getParent())) {
62
+ return false;
63
+ }
64
+ var listNode = listItemNode.getParentOrThrow();
65
+ queueMicrotask(function () {
66
+ editor.update(function () {
67
+ // Add null check since listItemNode might be undefined in this closure
68
+ if (!listItemNode) return;
69
+ var newlistNode;
70
+ var isFirst = listItemNode.getPreviousSibling() === null;
71
+ if (isFirst) {
72
+ var _p = listItemNode.replace($createParagraphNode(), true);
73
+ _p.select(0, 0);
74
+ return;
75
+ }
76
+ var next = listItemNode.getNextSibling();
77
+ if (next) {
78
+ newlistNode = $createListNode(listNode.getListType(), listItemNode.getValue());
79
+ }
80
+ while (next && newlistNode) {
81
+ next.remove();
82
+ newlistNode.append(next);
83
+ next = next.getNextSibling();
84
+ }
85
+ var p = listItemNode.replace($createParagraphNode(), true);
86
+ p.remove();
87
+ listNode.insertAfter(p);
88
+ if (newlistNode) {
89
+ p.insertAfter(newlistNode);
90
+ }
91
+ p.select(0, 0);
92
+ });
93
+ });
94
+ event.stopImmediatePropagation();
95
+ event.preventDefault();
96
+ return true;
97
+ }, COMMAND_PRIORITY_LOW));
98
+ }
@@ -11,7 +11,9 @@ import { useLexicalComposerContext } from "../../../editor-kernel/react/react-co
11
11
  import { MarkdownPlugin } from "../../markdown";
12
12
  import { ListPlugin } from "../plugin";
13
13
  import { useStyles } from "./style";
14
- var ReactListPlugin = function ReactListPlugin() {
14
+ var ReactListPlugin = function ReactListPlugin(_ref) {
15
+ var _ref$enableHotkey = _ref.enableHotkey,
16
+ enableHotkey = _ref$enableHotkey === void 0 ? true : _ref$enableHotkey;
15
17
  var _useLexicalComposerCo = useLexicalComposerContext(),
16
18
  _useLexicalComposerCo2 = _slicedToArray(_useLexicalComposerCo, 1),
17
19
  editor = _useLexicalComposerCo2[0];
@@ -20,9 +22,10 @@ var ReactListPlugin = function ReactListPlugin() {
20
22
  useLayoutEffect(function () {
21
23
  editor.registerPlugin(MarkdownPlugin);
22
24
  editor.registerPlugin(ListPlugin, {
25
+ enableHotkey: enableHotkey,
23
26
  theme: styles
24
27
  });
25
- }, []);
28
+ }, [enableHotkey, styles]);
26
29
  return null;
27
30
  };
28
31
  ReactListPlugin.displayName = 'ReactListPlugin';
@@ -1,3 +1,4 @@
1
1
  export interface ReactListPluginProps {
2
2
  className?: string;
3
+ enableHotkey?: boolean;
3
4
  }