@lobehub/editor 1.7.0 → 1.8.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 (30) hide show
  1. package/es/plugins/code/command/index.js +7 -7
  2. package/es/plugins/code/node/code.d.ts +2 -1
  3. package/es/plugins/code/node/code.js +23 -4
  4. package/es/plugins/code/plugin/index.js +13 -1
  5. package/es/plugins/code/plugin/registry.js +14 -7
  6. package/es/plugins/common/index.d.ts +1 -1
  7. package/es/plugins/common/index.js +1 -1
  8. package/es/plugins/common/node/cursor.js +8 -0
  9. package/es/plugins/common/plugin/index.js +0 -4
  10. package/es/plugins/markdown/service/shortcut.d.ts +5 -3
  11. package/es/plugins/markdown/service/shortcut.js +41 -33
  12. package/es/plugins/slash/plugin/index.js +28 -1
  13. package/es/plugins/slash/react/type.d.ts +19 -0
  14. package/es/plugins/slash/service/i-slash-service.d.ts +20 -2
  15. package/es/plugins/slash/service/i-slash-service.js +6 -3
  16. package/es/plugins/slash/utils/utils.js +8 -2
  17. package/es/plugins/table/react/TableActionMenu/style.js +1 -1
  18. package/es/plugins/table/react/TableHoverActions/index.js +4 -10
  19. package/es/plugins/table/react/TableHoverActions/style.js +2 -2
  20. package/es/react/ChatInput/ChatInput.js +124 -22
  21. package/es/react/ChatInput/style.d.ts +2 -0
  22. package/es/react/ChatInput/style.js +5 -3
  23. package/es/react/ChatInput/type.d.ts +14 -2
  24. package/es/react/ChatInputActions/components/useDisplayActionCount.d.ts +1 -1
  25. package/es/react/ChatInputActions/components/useDisplayActionCount.js +7 -7
  26. package/es/react/Editor/Editor.js +2 -2
  27. package/es/react/hooks/useSize.d.ts +13 -0
  28. package/es/react/{ChatInputActions/components/useContainerSize.js → hooks/useSize.js} +37 -3
  29. package/package.json +2 -1
  30. package/es/react/ChatInputActions/components/useContainerSize.d.ts +0 -9
@@ -4,7 +4,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
4
4
  import { $isCodeHighlightNode } from '@lexical/code';
5
5
  import { $getSelection, $insertNodes, $isRangeSelection, COMMAND_PRIORITY_EDITOR, createCommand } from 'lexical';
6
6
  import { $createCursorNode } from "../../common";
7
- import { $createCodeNode, $isCodeInlineNode } from "../node/code";
7
+ import { $createCodeNode, $isCodeInlineNode, getCodeInlineNode } from "../node/code";
8
8
  export var INSERT_CODEINLINE_COMMAND = createCommand('INSERT_CODEINLINE_COMMAND');
9
9
  export function registerCodeInlineCommand(editor) {
10
10
  return editor.registerCommand(INSERT_CODEINLINE_COMMAND, function () {
@@ -18,24 +18,24 @@ export function registerCodeInlineCommand(editor) {
18
18
  if ($isCodeHighlightNode(focusNode) || $isCodeHighlightNode(anchorNode)) {
19
19
  return false;
20
20
  }
21
- if (focusNode.getParent() !== anchorNode.getParent()) {
21
+ var code = getCodeInlineNode(focusNode);
22
+ if (code !== getCodeInlineNode(anchorNode)) {
22
23
  return false;
23
24
  }
24
- var parentNode = focusNode.getParent();
25
- if ($isCodeInlineNode(parentNode)) {
26
- var _iterator = _createForOfIteratorHelper(parentNode.getChildren().slice(0)),
25
+ if ($isCodeInlineNode(code)) {
26
+ var _iterator = _createForOfIteratorHelper(code.getChildren().slice(0)),
27
27
  _step;
28
28
  try {
29
29
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
30
30
  var node = _step.value;
31
- parentNode.insertBefore(node);
31
+ code.insertBefore(node);
32
32
  }
33
33
  } catch (err) {
34
34
  _iterator.e(err);
35
35
  } finally {
36
36
  _iterator.f();
37
37
  }
38
- parentNode.remove();
38
+ code.remove();
39
39
  return true;
40
40
  }
41
41
  var codeNode = $createCodeNode(selection.getTextContent());
@@ -1,4 +1,4 @@
1
- import { EditorConfig, LexicalEditor, SerializedElementNode } from 'lexical';
1
+ import { EditorConfig, LexicalEditor, LexicalNode, SerializedElementNode } from 'lexical';
2
2
  import { CardLikeElementNode } from "../../common";
3
3
  export type SerializedCodeNode = SerializedElementNode;
4
4
  export declare class CodeNode extends CardLikeElementNode {
@@ -17,4 +17,5 @@ export declare class CodeNode extends CardLikeElementNode {
17
17
  }
18
18
  export declare function $createCodeNode(textContent?: string): CodeNode;
19
19
  export declare function $isCodeInlineNode(node: unknown): node is CodeNode;
20
+ export declare function getCodeInlineNode(node: LexicalNode): LexicalNode | null;
20
21
  export declare function $isSelectionInCodeInline(editor: LexicalEditor): boolean;
@@ -16,7 +16,7 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
16
16
  /* eslint-disable @typescript-eslint/no-use-before-define */
17
17
  import { addClassNamesToElement } from '@lexical/utils';
18
18
  import { $applyNodeReplacement, $createTextNode, $getSelection, $isNodeSelection, $isRangeSelection } from 'lexical';
19
- import { $createCursorNode, CardLikeElementNode } from "../../common";
19
+ import { $createCursorNode, $isCursorNode, CardLikeElementNode } from "../../common";
20
20
  export var CodeNode = /*#__PURE__*/function (_CardLikeElementNode) {
21
21
  _inherits(CodeNode, _CardLikeElementNode);
22
22
  var _super = _createSuper(CodeNode);
@@ -120,6 +120,25 @@ export function $createCodeNode(textContent) {
120
120
  export function $isCodeInlineNode(node) {
121
121
  return node instanceof CodeNode;
122
122
  }
123
+ export function getCodeInlineNode(node) {
124
+ if ($isCursorNode(node)) {
125
+ var parent = node.getParent();
126
+ if ($isCodeInlineNode(parent)) {
127
+ return parent;
128
+ }
129
+ if ($isCodeInlineNode(node.getNextSibling())) {
130
+ return node.getNextSibling();
131
+ }
132
+ if ($isCodeInlineNode(node.getPreviousSibling())) {
133
+ return node.getPreviousSibling();
134
+ }
135
+ return null;
136
+ }
137
+ if ($isCodeInlineNode(node.getParent())) {
138
+ return node.getParent();
139
+ }
140
+ return null;
141
+ }
123
142
  export function $isSelectionInCodeInline(editor) {
124
143
  return editor.read(function () {
125
144
  var selection = $getSelection();
@@ -129,11 +148,11 @@ export function $isSelectionInCodeInline(editor) {
129
148
  if ($isRangeSelection(selection)) {
130
149
  var focusNode = selection.focus.getNode();
131
150
  var anchorNode = selection.anchor.getNode();
132
- if (focusNode.getParent() !== anchorNode.getParent()) {
151
+ var code = getCodeInlineNode(focusNode);
152
+ if (code !== getCodeInlineNode(anchorNode)) {
133
153
  return false;
134
154
  }
135
- var parentNode = focusNode.getParent();
136
- if ($isCodeInlineNode(parentNode)) {
155
+ if ($isCodeInlineNode(code)) {
137
156
  return true;
138
157
  }
139
158
  return false;
@@ -13,10 +13,12 @@ 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 { $setSelection } from 'lexical';
16
17
  import { KernelPlugin } from "../../../editor-kernel/plugin";
18
+ import { $createCursorNode } from "../../common";
17
19
  import { IMarkdownShortCutService } from "../../markdown/service/shortcut";
18
20
  import { registerCodeInlineCommand } from "../command";
19
- import { CodeNode } from "../node/code";
21
+ import { $createCodeNode, CodeNode } from "../node/code";
20
22
  import { registerCodeInline } from "./registry";
21
23
  export var CodePlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
22
24
  _inherits(CodePlugin, _KernelPlugin);
@@ -49,6 +51,16 @@ export var CodePlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
49
51
  ctx.appendLine("`".concat(node.getTextContent(), "`"));
50
52
  return true;
51
53
  });
54
+ markdownService.registerMarkdownShortCuts([{
55
+ process: function process(selection) {
56
+ var text = selection.getTextContent();
57
+ selection.removeText();
58
+ selection.insertNodes([$createCodeNode(text), $createCursorNode()]);
59
+ $setSelection(selection);
60
+ },
61
+ tag: '`',
62
+ type: 'text-format'
63
+ }]);
52
64
  }
53
65
  }]);
54
66
  return CodePlugin;
@@ -3,6 +3,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
3
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
4
  import { mergeRegister } from '@lexical/utils';
5
5
  import { $getNodeByKey } from 'lexical';
6
+ import { $createCursorNode } from "../../common";
6
7
  import { HotkeyEnum } from "../../../types/hotkey";
7
8
  import { INSERT_CODEINLINE_COMMAND } from "../command";
8
9
  import { CodeNode } from "../node/code";
@@ -14,6 +15,7 @@ export function registerCodeInline(editor, kernel, options) {
14
15
  var mutatedNodes = _ref2.mutatedNodes;
15
16
  var codeChanged = mutatedNodes === null || mutatedNodes === void 0 ? void 0 : mutatedNodes.get(CodeNode);
16
17
  var keys = (codeChanged === null || codeChanged === void 0 ? void 0 : codeChanged.keys()) || [];
18
+ var needAddBefore = new Set();
17
19
  editor.read(function () {
18
20
  var _iterator = _createForOfIteratorHelper(keys),
19
21
  _step;
@@ -25,13 +27,8 @@ export function registerCodeInline(editor, kernel, options) {
25
27
  return;
26
28
  }
27
29
  var parent = node.getParent();
28
- if ((parent === null || parent === void 0 ? void 0 : parent.__last) === key) {
29
- var codeElement = editor.getElementByKey(key);
30
- if (!(codeElement !== null && codeElement !== void 0 && codeElement.nextSibling)) {
31
- parent
32
- // @ts-expect-error not error
33
- .getDOMSlot(editor.getElementByKey(parent.getKey())).setManagedLineBreak('decorator');
34
- }
30
+ if ((parent === null || parent === void 0 ? void 0 : parent.getFirstChild()) === node) {
31
+ needAddBefore.add(node);
35
32
  }
36
33
  }
37
34
  } catch (err) {
@@ -40,6 +37,16 @@ export function registerCodeInline(editor, kernel, options) {
40
37
  _iterator.f();
41
38
  }
42
39
  });
40
+ if (needAddBefore.size > 0) {
41
+ editor.update(function () {
42
+ needAddBefore.forEach(function (node) {
43
+ var prev = node.getPreviousSibling();
44
+ if (!prev) {
45
+ node.insertBefore($createCursorNode());
46
+ }
47
+ });
48
+ });
49
+ }
43
50
  }), kernel.registerHotkey(HotkeyEnum.CodeInline, function () {
44
51
  return editor.dispatchCommand(INSERT_CODEINLINE_COMMAND, undefined);
45
52
  }, {
@@ -1,4 +1,4 @@
1
1
  export { INSERT_HEADING_COMMAND, INSERT_QUOTE_COMMAND } from './command';
2
- export { $createCursorNode, $isCardLikeElementNode, CardLikeElementNode } from './node/cursor';
2
+ export { $createCursorNode, $isCardLikeElementNode, $isCursorNode, CardLikeElementNode, } from './node/cursor';
3
3
  export * from './plugin';
4
4
  export * from './react';
@@ -1,4 +1,4 @@
1
1
  export { INSERT_HEADING_COMMAND, INSERT_QUOTE_COMMAND } from "./command";
2
- export { $createCursorNode, $isCardLikeElementNode, CardLikeElementNode } from "./node/cursor";
2
+ export { $createCursorNode, $isCardLikeElementNode, $isCursorNode, CardLikeElementNode } from "./node/cursor";
3
3
  export * from "./plugin";
4
4
  export * from "./react";
@@ -130,6 +130,14 @@ export function registerCursorNode(editor) {
130
130
  mutation = _step3$value[1];
131
131
  if (mutation === 'updated') {
132
132
  var cursorNode = $getNodeByKey(key);
133
+ if ((cursorNode === null || cursorNode === void 0 ? void 0 : cursorNode.getIndexWithinParent()) === 0) {
134
+ var nextElement = cursorNode.getNextSibling();
135
+ if (!$isCardLikeElementNode(nextElement) && !$isDecoratorNode(nextElement) && !$isCardLikeElementNode(cursorNode === null || cursorNode === void 0 ? void 0 : cursorNode.getParent())) {
136
+ needRemove.add(cursorNode);
137
+ } else {
138
+ continue;
139
+ }
140
+ }
133
141
  var element = cursorNode === null || cursorNode === void 0 ? void 0 : cursorNode.getPreviousSibling();
134
142
  if (!$isCardLikeElementNode(element) && !$isDecoratorNode(element) && !$isCardLikeElementNode(cursorNode === null || cursorNode === void 0 ? void 0 : cursorNode.getParent())) {
135
143
  needRemove.add(cursorNode);
@@ -117,10 +117,6 @@ export var CommonPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
117
117
  intraword: false,
118
118
  tag: '__',
119
119
  type: 'text-format'
120
- }, {
121
- format: ['code'],
122
- tag: '`',
123
- type: 'text-format'
124
120
  }, {
125
121
  format: ['strikethrough'],
126
122
  tag: '~~',
@@ -1,8 +1,9 @@
1
- import { ElementNode, LexicalNode, TextFormatType, TextNode } from 'lexical';
1
+ import { ElementNode, LexicalNode, RangeSelection, TextFormatType, TextNode } from 'lexical';
2
2
  import { IServiceID } from "../../../types/kernel";
3
3
  export type TextFormatTransformer = Readonly<{
4
- format: ReadonlyArray<TextFormatType>;
4
+ format?: ReadonlyArray<TextFormatType>;
5
5
  intraword?: boolean;
6
+ process?: (selection: RangeSelection) => void;
6
7
  tag: string;
7
8
  type: 'text-format';
8
9
  }>;
@@ -129,8 +130,9 @@ export declare class MarkdownShortCutService implements IMarkdownShortCutService
129
130
  type: "text-match";
130
131
  }>[]>>;
131
132
  get textFormatTransformersByTrigger(): Readonly<Record<string, readonly Readonly<{
132
- format: readonly TextFormatType[];
133
+ format?: readonly TextFormatType[] | undefined;
133
134
  intraword?: boolean | undefined;
135
+ process?: ((selection: RangeSelection) => void) | undefined;
134
136
  tag: string;
135
137
  type: "text-format";
136
138
  }>[]>>;
@@ -234,50 +234,58 @@ function $runTextFormatTransformers(anchorNode, anchorOffset, textFormatTransfor
234
234
  closeNode.setTextContent(closeNodeText);
235
235
  var openNodeText = openNode === closeNode ? closeNodeText : prevOpenNodeText;
236
236
  openNode.setTextContent(openNodeText.slice(0, openTagStartIndex) + openNodeText.slice(openTagStartIndex + tagLength));
237
- var selection = $getSelection();
237
+ var _selection = $getSelection();
238
238
  var nextSelection = $createRangeSelection();
239
239
  $setSelection(nextSelection);
240
240
  // Adjust offset based on deleted chars
241
241
  var newOffset = closeTagEndIndex - tagLength * (openNode === closeNode ? 2 : 1) + 1;
242
242
  nextSelection.anchor.set(openNode.__key, openTagStartIndex, 'text');
243
243
  nextSelection.focus.set(closeNode.__key, newOffset, 'text');
244
-
245
- // Apply formatting to selected text
246
- var _iterator5 = _createForOfIteratorHelper(matcher.format),
247
- _step5;
248
- try {
249
- for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
250
- var format = _step5.value;
251
- if (!nextSelection.hasFormat(format)) {
252
- nextSelection.formatText(format);
244
+ if (matcher.process) {
245
+ matcher.process(nextSelection);
246
+ return true;
247
+ } else if (matcher.format) {
248
+ // Apply formatting to selected text
249
+ var _iterator5 = _createForOfIteratorHelper(matcher.format),
250
+ _step5;
251
+ try {
252
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
253
+ var format = _step5.value;
254
+ if (!nextSelection.hasFormat(format)) {
255
+ nextSelection.formatText(format);
256
+ }
253
257
  }
254
- }
255
258
 
256
- // Collapse selection up to the focus point
257
- } catch (err) {
258
- _iterator5.e(err);
259
- } finally {
260
- _iterator5.f();
261
- }
262
- nextSelection.anchor.set(nextSelection.focus.key, nextSelection.focus.offset, nextSelection.focus.type);
259
+ // Collapse selection up to the focus point
260
+ } catch (err) {
261
+ _iterator5.e(err);
262
+ } finally {
263
+ _iterator5.f();
264
+ }
265
+ nextSelection.anchor.set(nextSelection.focus.key, nextSelection.focus.offset, nextSelection.focus.type);
263
266
 
264
- // Remove formatting from collapsed selection
265
- var _iterator6 = _createForOfIteratorHelper(matcher.format),
266
- _step6;
267
- try {
268
- for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
269
- var _format = _step6.value;
270
- if (nextSelection.hasFormat(_format)) {
271
- nextSelection.toggleFormat(_format);
267
+ // Remove formatting from collapsed selection
268
+ var _iterator6 = _createForOfIteratorHelper(matcher.format),
269
+ _step6;
270
+ try {
271
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
272
+ var _format = _step6.value;
273
+ if (nextSelection.hasFormat(_format)) {
274
+ nextSelection.toggleFormat(_format);
275
+ }
272
276
  }
277
+ } catch (err) {
278
+ _iterator6.e(err);
279
+ } finally {
280
+ _iterator6.f();
273
281
  }
274
- } catch (err) {
275
- _iterator6.e(err);
276
- } finally {
277
- _iterator6.f();
278
- }
279
- if ($isRangeSelection(selection)) {
280
- nextSelection.format = selection.format;
282
+ if ($isRangeSelection(_selection)) {
283
+ nextSelection.format = _selection.format;
284
+ }
285
+ } else {
286
+ // No format or process specified, nothing to do
287
+ $setSelection(_selection);
288
+ continue;
281
289
  }
282
290
  return true;
283
291
  }
@@ -15,7 +15,7 @@ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.g
15
15
  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; }
16
16
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
17
17
  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); }
18
- import { $getSelection, $isRangeSelection } from 'lexical';
18
+ import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_CRITICAL, KEY_DOWN_COMMAND } from 'lexical';
19
19
  import { KernelPlugin } from "../../../editor-kernel/plugin";
20
20
  import { ISlashService, SlashService } from "../service/i-slash-service";
21
21
  import { getQueryTextForSearch, tryToPositionRange } from "../utils/utils";
@@ -29,6 +29,7 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
29
29
  _defineProperty(_assertThisInitialized(_this), "service", null);
30
30
  _defineProperty(_assertThisInitialized(_this), "currentSlashTrigger", null);
31
31
  _defineProperty(_assertThisInitialized(_this), "currentSlashTriggerIndex", -1);
32
+ _defineProperty(_assertThisInitialized(_this), "suppressOpen", false);
32
33
  _this.config = config;
33
34
  _this.service = new SlashService(kernel);
34
35
  kernel.registerService(ISlashService, _this.service);
@@ -47,11 +48,23 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
47
48
  (_this$config = this.config) === null || _this$config === void 0 || _this$config.triggerClose();
48
49
  this.currentSlashTrigger = null;
49
50
  this.currentSlashTriggerIndex = -1;
51
+ // After an explicit close, suppress reopening until next typing input
52
+ this.suppressOpen = true;
50
53
  }
51
54
  }, {
52
55
  key: "onInit",
53
56
  value: function onInit(editor) {
54
57
  var _this2 = this;
58
+ // Reset suppression on typing-related key presses
59
+ this.register(editor.registerCommand(KEY_DOWN_COMMAND, function (event) {
60
+ if (event.isComposing) return false;
61
+ var key = event.key;
62
+ // Any character input or deletion should re-enable opening
63
+ if (key.length === 1 || key === 'Backspace' || key === 'Delete') {
64
+ _this2.suppressOpen = false;
65
+ }
66
+ return false;
67
+ }, COMMAND_PRIORITY_CRITICAL));
55
68
  this.register(editor.registerUpdateListener(function () {
56
69
  editor.getEditorState().read(function () {
57
70
  var _this2$service, _this2$service2, _this2$service3;
@@ -79,6 +92,12 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
79
92
  _this2.triggerClose();
80
93
  return;
81
94
  }
95
+
96
+ // If we previously suppressed opening, do not reopen until user types
97
+ if (_this2.currentSlashTrigger === null && _this2.suppressOpen) {
98
+ _this2.triggerClose();
99
+ return;
100
+ }
82
101
  var triggerText = _this2.currentSlashTrigger;
83
102
  if (triggerText === null) {
84
103
  triggerText = text.slice(-1);
@@ -101,6 +120,14 @@ export var SlashPlugin = (_class = /*#__PURE__*/function (_KernelPlugin) {
101
120
  var fuse = (_this2$service3 = _this2.service) === null || _this2$service3 === void 0 ? void 0 : _this2$service3.getSlashFuse(triggerText);
102
121
  var isRangePositioned = tryToPositionRange(_this2.currentSlashTriggerIndex, range, editorWindow);
103
122
  var match = triggerFn === null || triggerFn === void 0 ? void 0 : triggerFn(text.slice(_this2.currentSlashTriggerIndex));
123
+
124
+ // Check if there's a space in the current search text that should close the menu
125
+ var searchText = text.slice(_this2.currentSlashTriggerIndex);
126
+ var hasSpaceAfterTrigger = searchText.includes(' ') && !(slashOptions !== null && slashOptions !== void 0 && slashOptions.allowWhitespace);
127
+ if (hasSpaceAfterTrigger) {
128
+ _this2.triggerClose();
129
+ return;
130
+ }
104
131
  var finalItems = fuse && match && match.matchingString.length > 0 ? fuse.search(match.matchingString).map(function (result) {
105
132
  return result.item;
106
133
  }) : slashOptions.items;
@@ -1,8 +1,20 @@
1
+ import type { IFuseOptions } from 'fuse.js';
1
2
  import type { FC, ReactElement } from 'react';
2
3
  import type { SlashOptions } from "..";
3
4
  import type { ISlashMenuOption, ISlashOption } from "../service/i-slash-service";
4
5
  import type { IEditor } from "../../../types";
5
6
  export interface ReactSlashOptionProps {
7
+ /**
8
+ * Complete Fuse.js configuration options
9
+ * @example
10
+ * {
11
+ * keys: ['key', 'label', 'description'],
12
+ * threshold: 0.3,
13
+ * includeScore: true,
14
+ * includeMatches: true
15
+ * }
16
+ */
17
+ fuseOptions?: IFuseOptions<ISlashMenuOption>;
6
18
  /**
7
19
  * Searchable options
8
20
  */
@@ -17,6 +29,13 @@ export interface ReactSlashOptionProps {
17
29
  * Custom render component
18
30
  */
19
31
  renderComp?: FC<MenuRenderProps>;
32
+ /**
33
+ * Fuse.js search keys for fuzzy matching
34
+ * Default is ['key']
35
+ * @example ['key', 'label', 'description']
36
+ * @deprecated Use fuseOptions instead
37
+ */
38
+ searchKeys?: string[];
20
39
  /**
21
40
  * Trigger character
22
41
  */
@@ -1,17 +1,28 @@
1
1
  import { DropdownMenuItemType } from '@lobehub/ui';
2
- import Fuse from 'fuse.js';
2
+ import Fuse, { type IFuseOptions } from 'fuse.js';
3
3
  import type { IEditor, IEditorKernel, IServiceID } from "../../../types";
4
4
  import { getBasicTypeaheadTriggerMatch } from '../utils/utils';
5
5
  export type ISlashDividerOption = {
6
6
  type: 'divider';
7
7
  };
8
- export interface ISlashMenuOption extends Omit<DropdownMenuItemType, 'extra'> {
8
+ export interface ISlashMenuOption extends DropdownMenuItemType {
9
9
  metadata?: Record<string, any>;
10
10
  onSelect?: (editor: IEditor, matchingString: string) => void;
11
11
  }
12
12
  export type ISlashOption = ISlashMenuOption | ISlashDividerOption;
13
13
  export interface SlashOptions {
14
14
  allowWhitespace?: boolean;
15
+ /**
16
+ * Complete Fuse.js configuration options
17
+ * @example
18
+ * {
19
+ * keys: ['key', 'label', 'description'],
20
+ * threshold: 0.3,
21
+ * includeScore: true,
22
+ * includeMatches: true
23
+ * }
24
+ */
25
+ fuseOptions?: IFuseOptions<ISlashMenuOption>;
15
26
  items: Array<ISlashOption> | ((search: {
16
27
  leadOffset: number;
17
28
  matchingString: string;
@@ -20,6 +31,13 @@ export interface SlashOptions {
20
31
  maxLength?: number;
21
32
  minLength?: number;
22
33
  punctuation?: string;
34
+ /**
35
+ * Fuse.js search keys for fuzzy matching
36
+ * Default is ['key']
37
+ * @example ['key', 'label', 'description']
38
+ * @deprecated Use fuseOptions instead
39
+ */
40
+ searchKeys?: string[];
23
41
  trigger: string;
24
42
  }
25
43
  export interface ISlashService {
@@ -46,9 +46,12 @@ export var SlashService = /*#__PURE__*/function () {
46
46
  var searchableItems = options.items.filter(function (item) {
47
47
  return !('type' in item) || item.type !== 'divider';
48
48
  });
49
- this.triggerFuseMap.set(options.trigger, new Fuse(searchableItems, {
50
- keys: ['label', 'value']
51
- }));
49
+
50
+ // Use fuseOptions if provided, otherwise fallback to searchKeys or default
51
+ var fuseConfig = options.fuseOptions || {
52
+ keys: options.searchKeys || ['key']
53
+ };
54
+ this.triggerFuseMap.set(options.trigger, new Fuse(searchableItems, fuseConfig));
52
55
  }
53
56
  }
54
57
  }, {
@@ -120,14 +120,20 @@ export function getBasicTypeaheadTriggerMatch(trigger, _ref) {
120
120
  _ref$allowWhitespace = _ref.allowWhitespace,
121
121
  allowWhitespace = _ref$allowWhitespace === void 0 ? false : _ref$allowWhitespace;
122
122
  return function (text) {
123
+ // When whitespace is not allowed, we need to ensure the regex stops at whitespace
123
124
  var validCharsSuffix = allowWhitespace ? '' : '\\s';
124
125
  var validChars = '[^' + trigger + punctuation + validCharsSuffix + ']';
125
- var TypeaheadTriggerRegex = new RegExp('(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + '){0,' + maxLength + '})' + ')$');
126
+
127
+ // Create regex that matches from trigger to either end of string OR whitespace (when not allowed)
128
+ var regexPattern = allowWhitespace ? '(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + '){0,' + maxLength + '})' + ')$' : '(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + ')*?)' + ')(?=\\s|$)';
129
+ var TypeaheadTriggerRegex = new RegExp(regexPattern);
126
130
  var match = TypeaheadTriggerRegex.exec(text);
127
131
  if (match !== null) {
128
132
  var maybeLeadingWhitespace = match[1];
129
133
  var matchingString = match[3];
130
- if (matchingString.length >= minLength) {
134
+
135
+ // Check length constraints
136
+ if (matchingString.length >= minLength && matchingString.length <= maxLength) {
131
137
  return {
132
138
  leadOffset: match.index + maybeLeadingWhitespace.length,
133
139
  matchingString: matchingString,
@@ -3,5 +3,5 @@ function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(
3
3
  import { createStyles } from 'antd-style';
4
4
  export var useStyles = createStyles(function (_ref) {
5
5
  var css = _ref.css;
6
- return css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n will-change: transform;\n\n position: absolute;\n z-index: 3;\n inset-block-start: 0;\n inset-inline-start: 0;\n "])));
6
+ return css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n will-change: transform;\n\n position: absolute;\n z-index: 3;\n inset-block-start: 0;\n inset-inline-start: 0;\n\n background: red;\n "])));
7
7
  });
@@ -19,7 +19,7 @@ import { PlusIcon } from 'lucide-react';
19
19
  import { memo, useEffect, useMemo, useRef, useState } from 'react';
20
20
  import { createPortal } from 'react-dom';
21
21
  import { useLexicalComposerContext } from "../../../../editor-kernel/react";
22
- import { BUTTON_WIDTH_PX, useStyles } from "./style";
22
+ import { useStyles } from "./style";
23
23
  import { getMouseInfo, useDebounce } from "./utils";
24
24
  import { jsx as _jsx } from "react/jsx-runtime";
25
25
  import { jsxs as _jsxs } from "react/jsx-runtime";
@@ -94,12 +94,10 @@ var TableHoverActionsContainer = /*#__PURE__*/memo(function (_ref) {
94
94
  });
95
95
  if (tableDOMElement) {
96
96
  var _getBoundingClientRec = tableDOMElement.getBoundingClientRect(),
97
- tableElemWidth = _getBoundingClientRec.width,
98
97
  tableElemY = _getBoundingClientRec.y,
99
98
  tableElemRight = _getBoundingClientRec.right,
100
99
  tableElemLeft = _getBoundingClientRec.left,
101
- tableElemBottom = _getBoundingClientRec.bottom,
102
- tableElemHeight = _getBoundingClientRec.height;
100
+ tableElemBottom = _getBoundingClientRec.bottom;
103
101
 
104
102
  // Adjust for using the scrollable table container
105
103
  var parentElement = tableDOMElement.parentElement;
@@ -114,19 +112,15 @@ var TableHoverActionsContainer = /*#__PURE__*/memo(function (_ref) {
114
112
  setShownColumn(false);
115
113
  setShownRow(true);
116
114
  setPosition({
117
- height: BUTTON_WIDTH_PX,
118
115
  left: tableHasScroll && parentElement ? parentElement.offsetLeft : tableElemLeft - editorElemLeft,
119
- top: tableElemBottom - editorElemY + 5,
120
- width: tableHasScroll && parentElement ? parentElement.offsetWidth : tableElemWidth
116
+ top: tableElemBottom - editorElemY + 5
121
117
  });
122
118
  } else if (hoveredColumnNode) {
123
119
  setShownColumn(true);
124
120
  setShownRow(false);
125
121
  setPosition({
126
- height: tableElemHeight,
127
122
  left: tableElemRight - editorElemLeft + 5,
128
- top: tableElemY - editorElemY,
129
- width: BUTTON_WIDTH_PX
123
+ top: tableElemY - editorElemY
130
124
  });
131
125
  }
132
126
  }
@@ -5,7 +5,7 @@ export var BUTTON_WIDTH_PX = 20;
5
5
  export var useStyles = createStyles(function (_ref) {
6
6
  var css = _ref.css;
7
7
  return {
8
- tableAddColumns: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: absolute;\n height: 100%;\n "]))),
9
- tableAddRows: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n position: absolute;\n width: calc(100% - 25px);\n "])))
8
+ tableAddColumns: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: absolute;\n "]))),
9
+ tableAddRows: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n position: absolute;\n "])))
10
10
  };
11
11
  });
@@ -1,50 +1,152 @@
1
1
  'use client';
2
2
 
3
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
- var _excluded = ["maxHeight", "className", "children", "footer", "header", "style", "slashMenuRef", "classNames", "fullscreen", "styles"];
4
+ var _excluded = ["defaultHeight", "height", "maxHeight", "minHeight", "resizeMaxHeightOffset", "resize", "onSizeChange", "onSizeDragging", "className", "children", "footer", "header", "style", "slashMenuRef", "classNames", "fullscreen", "showResizeHandle", "styles"];
5
5
  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; }
6
6
  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; }
7
7
  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; }
8
8
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
9
9
  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); }
10
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
11
+ 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."); }
12
+ 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); }
13
+ 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; }
14
+ 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; } }
15
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
10
16
  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; }
11
17
  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; }
12
- import { memo } from 'react';
18
+ import { Resizable } from 're-resizable';
19
+ import { memo, useCallback } from 'react';
13
20
  import { Flexbox } from 'react-layout-kit';
21
+ import useMergeState from 'use-merge-value';
22
+ import { useHeight } from "../hooks/useSize";
14
23
  import { useStyles } from "./style";
15
24
  import { jsx as _jsx } from "react/jsx-runtime";
16
25
  import { jsxs as _jsxs } from "react/jsx-runtime";
17
- var ChatInput = /*#__PURE__*/memo(function (_ref) {
18
- var _ref$maxHeight = _ref.maxHeight,
19
- maxHeight = _ref$maxHeight === void 0 ? 'min(50vh, 640px)' : _ref$maxHeight,
20
- className = _ref.className,
21
- children = _ref.children,
22
- footer = _ref.footer,
23
- header = _ref.header,
24
- style = _ref.style,
25
- slashMenuRef = _ref.slashMenuRef,
26
- classNames = _ref.classNames,
27
- fullscreen = _ref.fullscreen,
28
- customStyles = _ref.styles,
29
- rest = _objectWithoutProperties(_ref, _excluded);
26
+ var ChatInput = /*#__PURE__*/memo(function (props) {
27
+ var _props$defaultHeight = props.defaultHeight,
28
+ defaultHeight = _props$defaultHeight === void 0 ? props.defaultHeight || props.minHeight || 64 : _props$defaultHeight,
29
+ height = props.height,
30
+ _props$maxHeight = props.maxHeight,
31
+ maxHeight = _props$maxHeight === void 0 ? 320 : _props$maxHeight,
32
+ _props$minHeight = props.minHeight,
33
+ minHeight = _props$minHeight === void 0 ? 64 : _props$minHeight,
34
+ _props$resizeMaxHeigh = props.resizeMaxHeightOffset,
35
+ resizeMaxHeightOffset = _props$resizeMaxHeigh === void 0 ? 120 : _props$resizeMaxHeigh,
36
+ _props$resize = props.resize,
37
+ resize = _props$resize === void 0 ? true : _props$resize,
38
+ onSizeChange = props.onSizeChange,
39
+ onSizeDragging = props.onSizeDragging,
40
+ className = props.className,
41
+ children = props.children,
42
+ footer = props.footer,
43
+ header = props.header,
44
+ style = props.style,
45
+ slashMenuRef = props.slashMenuRef,
46
+ classNames = props.classNames,
47
+ fullscreen = props.fullscreen,
48
+ showResizeHandle = props.showResizeHandle,
49
+ customStyles = props.styles,
50
+ rest = _objectWithoutProperties(props, _excluded);
30
51
  var _useStyles = useStyles(),
31
52
  cx = _useStyles.cx,
32
53
  styles = _useStyles.styles;
54
+ var _useHeight = useHeight(),
55
+ headerRef = _useHeight.ref,
56
+ _useHeight$height = _useHeight.height,
57
+ headerHeight = _useHeight$height === void 0 ? 0 : _useHeight$height;
58
+
59
+ // 使用 useMergeState 管理高度状态
60
+ var _useMergeState = useMergeState(defaultHeight, {
61
+ defaultValue: defaultHeight,
62
+ onChange: onSizeChange,
63
+ value: height
64
+ }),
65
+ _useMergeState2 = _slicedToArray(_useMergeState, 2),
66
+ currentHeight = _useMergeState2[0],
67
+ setCurrentHeight = _useMergeState2[1];
68
+
69
+ // 处理尺寸变化
70
+ var handleResizeStop = useCallback(function (e, direction, ref) {
71
+ var newHeight = ref.style.height ? parseInt(ref.style.height) : defaultHeight;
72
+ setCurrentHeight(newHeight);
73
+ }, [setCurrentHeight, defaultHeight]);
74
+
75
+ // 处理拖拽过程中的尺寸变化
76
+ var handleResize = useCallback(function (e, direction, ref) {
77
+ var newHeight = ref.style.height ? parseInt(ref.style.height) : defaultHeight;
78
+ onSizeDragging === null || onSizeDragging === void 0 || onSizeDragging(newHeight);
79
+ }, [onSizeDragging, defaultHeight]);
80
+ var bodyNode = /*#__PURE__*/_jsx("div", {
81
+ className: cx(styles.editor, classNames === null || classNames === void 0 ? void 0 : classNames.body),
82
+ style: _objectSpread(_objectSpread({}, customStyles === null || customStyles === void 0 ? void 0 : customStyles.body), {}, {
83
+ flex: 1,
84
+ maxHeight: fullscreen ? '100%' : maxHeight,
85
+ minHeight: resize ? currentHeight : minHeight,
86
+ zIndex: 0
87
+ }),
88
+ children: children
89
+ });
33
90
  return /*#__PURE__*/_jsxs(Flexbox, _objectSpread(_objectSpread({
34
91
  className: cx(styles.container, className),
35
92
  height: fullscreen ? '100%' : undefined,
36
93
  style: _objectSpread({
37
- maxHeight: fullscreen ? undefined : maxHeight
94
+ position: 'relative'
38
95
  }, style),
39
- width: '100%'
96
+ width: "100%"
40
97
  }, rest), {}, {
41
98
  children: [slashMenuRef && /*#__PURE__*/_jsx("div", {
42
99
  ref: slashMenuRef
43
- }), header, /*#__PURE__*/_jsx("div", {
44
- className: cx(styles.editor, classNames === null || classNames === void 0 ? void 0 : classNames.body),
45
- style: customStyles === null || customStyles === void 0 ? void 0 : customStyles.body,
46
- children: children
47
- }), footer]
100
+ }), /*#__PURE__*/_jsx("div", {
101
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.header,
102
+ ref: headerRef,
103
+ style: _objectSpread({
104
+ width: '100%',
105
+ zIndex: 1
106
+ }, customStyles === null || customStyles === void 0 ? void 0 : customStyles.header),
107
+ children: header
108
+ }), resize ? /*#__PURE__*/_jsx(Resizable, {
109
+ className: styles.resizableContainer,
110
+ enable: fullscreen ? false : {
111
+ top: true
112
+ },
113
+ handleClasses: {
114
+ top: showResizeHandle ? styles.resizeHandle : undefined
115
+ },
116
+ handleStyles: {
117
+ top: {
118
+ backgroundColor: 'transparent',
119
+ borderRadius: '4px',
120
+ cursor: 'ns-resize',
121
+ height: '8px',
122
+ left: '50%',
123
+ top: !!header ? -3 - headerHeight : -3,
124
+ transform: 'translateX(-50%)',
125
+ width: '100%'
126
+ }
127
+ },
128
+ maxHeight: fullscreen ? undefined : maxHeight + resizeMaxHeightOffset,
129
+ minHeight: fullscreen ? undefined : minHeight,
130
+ onResize: handleResize,
131
+ onResizeStop: handleResizeStop,
132
+ size: {
133
+ height: fullscreen ? undefined : 'auto',
134
+ width: '100%'
135
+ },
136
+ style: fullscreen ? {
137
+ flex: 1,
138
+ overflow: 'hidden',
139
+ position: 'relative'
140
+ } : {},
141
+ children: bodyNode
142
+ }) : bodyNode, /*#__PURE__*/_jsx("div", {
143
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.footer,
144
+ style: _objectSpread({
145
+ width: '100%',
146
+ zIndex: 1
147
+ }, customStyles === null || customStyles === void 0 ? void 0 : customStyles.footer),
148
+ children: footer
149
+ })]
48
150
  }));
49
151
  });
50
152
  ChatInput.displayName = 'ChatInput';
@@ -1,4 +1,6 @@
1
1
  export declare const useStyles: (props?: unknown) => import("antd-style").ReturnStyles<{
2
2
  container: import("antd-style").SerializedStyles;
3
3
  editor: import("antd-style").SerializedStyles;
4
+ resizableContainer: import("antd-style").SerializedStyles;
5
+ resizeHandle: import("antd-style").SerializedStyles;
4
6
  }>;
@@ -1,11 +1,13 @@
1
- var _templateObject, _templateObject2;
1
+ var _templateObject, _templateObject2, _templateObject3, _templateObject4;
2
2
  function _taggedTemplateLiteral(strings, raw) { if (!raw) { raw = strings.slice(0); } return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
3
3
  import { createStyles } from 'antd-style';
4
4
  export var useStyles = createStyles(function (_ref) {
5
5
  var css = _ref.css,
6
6
  token = _ref.token;
7
7
  return {
8
- container: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: relative;\n\n border: 1px solid ", ";\n border-radius: ", "px;\n\n background-color: ", ";\n box-shadow:\n ", ",\n 0 32px 0 ", ";\n "])), token.colorBorderSecondary, token.borderRadiusLG, token.colorBgContainer, token.boxShadowTertiary, token.colorBgContainerSecondary),
9
- editor: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n overflow: hidden auto;\n flex: 1;\n\n width: 100%;\n padding-block: 8px 0;\n padding-inline: 12px;\n "])))
8
+ container: css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n position: relative;\n\n display: flex;\n flex-direction: column;\n\n height: 100%;\n border: 1px solid ", ";\n border-radius: ", "px;\n\n background-color: ", ";\n box-shadow:\n ", ",\n 0 32px 0 ", ";\n "])), token.colorBorderSecondary, token.borderRadiusLG, token.colorBgContainer, token.boxShadowTertiary, token.colorBgContainerSecondary),
9
+ editor: css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n overflow: hidden auto;\n flex: 1;\n\n width: 100%;\n padding-block: 8px 0;\n padding-inline: 12px;\n "]))),
10
+ resizableContainer: css(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\n position: relative;\n\n display: flex;\n flex-direction: column;\n align-self: flex-end;\n\n width: 100%;\n\n &:hover .resize-handle {\n opacity: 1;\n }\n "]))),
11
+ resizeHandle: css(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["\n position: absolute;\n inset-block-start: -4px;\n inset-inline-start: 50%;\n transform: translateX(-50%);\n\n width: 100%;\n height: 8px;\n\n opacity: 0;\n\n transition: opacity 0.2s ease-in-out;\n\n &::before {\n content: '';\n\n position: absolute;\n inset-block-start: 0;\n inset-inline-start: 50%;\n transform: translateX(-50%);\n\n width: 32px;\n height: 4px;\n border-radius: 4px;\n\n background-color: ", ";\n box-shadow: 0 1px 2px ", "20;\n }\n\n &:hover {\n opacity: 1 !important;\n\n &::before {\n background-color: ", ";\n box-shadow: 0 2px 4px ", "40;\n }\n }\n\n &:active {\n &::before {\n background-color: ", ";\n }\n }\n "])), token.colorPrimary, token.colorTextSecondary, token.colorPrimaryHover, token.colorTextSecondary, token.colorPrimaryActive)
10
12
  };
11
13
  });
@@ -1,15 +1,27 @@
1
1
  import type { CSSProperties, ReactNode, Ref } from 'react';
2
2
  import { FlexboxProps } from 'react-layout-kit';
3
- export interface ChatInputProps extends FlexboxProps {
3
+ export interface ChatInputProps extends Omit<FlexboxProps, 'height'> {
4
4
  classNames?: {
5
5
  body?: string;
6
+ footer?: string;
7
+ header?: string;
6
8
  };
9
+ defaultHeight?: number;
7
10
  footer?: ReactNode;
8
11
  fullscreen?: boolean;
9
12
  header?: ReactNode;
10
- maxHeight?: string | number;
13
+ height?: number;
14
+ maxHeight?: number;
15
+ minHeight?: number;
16
+ onSizeChange?: (height: number) => void;
17
+ onSizeDragging?: (height: number) => void;
18
+ resize?: boolean;
19
+ resizeMaxHeightOffset?: number;
20
+ showResizeHandle?: boolean;
11
21
  slashMenuRef?: Ref<HTMLDivElement>;
12
22
  styles?: {
13
23
  body?: CSSProperties;
24
+ footer?: CSSProperties;
25
+ header?: CSSProperties;
14
26
  };
15
27
  }
@@ -8,6 +8,6 @@ interface UseDisplayActionCountOptions {
8
8
  export declare const useDisplayActionCount: ({ items, collapseOffset, autoCollapse, }?: UseDisplayActionCountOptions) => {
9
9
  collapsed: boolean;
10
10
  maxCount: number;
11
- ref: import("react").RefObject<HTMLElement | null>;
11
+ ref: import("react").RefObject<HTMLDivElement | null>;
12
12
  };
13
13
  export {};
@@ -5,7 +5,7 @@ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len
5
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
6
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
7
  import { useEffect, useMemo, useState } from 'react';
8
- import { useContainerSize } from "./useContainerSize";
8
+ import { useWidth } from "../../hooks/useSize";
9
9
  export var useDisplayActionCount = function useDisplayActionCount() {
10
10
  var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
11
11
  _ref$items = _ref.items,
@@ -13,9 +13,9 @@ export var useDisplayActionCount = function useDisplayActionCount() {
13
13
  _ref$collapseOffset = _ref.collapseOffset,
14
14
  collapseOffset = _ref$collapseOffset === void 0 ? 0 : _ref$collapseOffset,
15
15
  autoCollapse = _ref.autoCollapse;
16
- var _useContainerSize = useContainerSize(),
17
- ref = _useContainerSize.ref,
18
- size = _useContainerSize.size;
16
+ var _useWidth = useWidth(),
17
+ ref = _useWidth.ref,
18
+ width = _useWidth.width;
19
19
  var _useState = useState(false),
20
20
  _useState2 = _slicedToArray(_useState, 2),
21
21
  collapsed = _useState2[0],
@@ -48,14 +48,14 @@ export var useDisplayActionCount = function useDisplayActionCount() {
48
48
  setMaxCount(rawMaxCount);
49
49
  return;
50
50
  }
51
- if (!size) return;
51
+ if (!width) return;
52
52
  var atLeastCount = 1 + alwaysDisplayCount;
53
- var calcMaxCount = Math.floor((size - collapseOffset) / 38);
53
+ var calcMaxCount = Math.floor((width - collapseOffset) / 38);
54
54
  if (calcMaxCount < atLeastCount) calcMaxCount = atLeastCount;
55
55
  setCollapsed(calcMaxCount < rawMaxCount);
56
56
  if (calcMaxCount >= rawMaxCount) return;
57
57
  setMaxCount(calcMaxCount);
58
- }, [autoCollapse, size, rawMaxCount, collapseOffset, alwaysDisplayCount]);
58
+ }, [autoCollapse, width, rawMaxCount, collapseOffset, alwaysDisplayCount]);
59
59
  return useMemo(function () {
60
60
  return {
61
61
  collapsed: collapsed,
@@ -71,10 +71,10 @@ var Editor = /*#__PURE__*/memo(function (_ref) {
71
71
  if (!enableSlash && !enableMention) return null;
72
72
  return /*#__PURE__*/_jsxs(ReactSlashPlugin, {
73
73
  children: [enableSlash ? /*#__PURE__*/_jsx(ReactSlashOption, _objectSpread({
74
- maxLength: 1,
74
+ maxLength: 8,
75
75
  trigger: "/"
76
76
  }, slashOption)) : undefined, enableMention ? /*#__PURE__*/_jsx(ReactSlashOption, _objectSpread({
77
- maxLength: 6,
77
+ maxLength: 8,
78
78
  trigger: "@"
79
79
  }, restMentionOption)) : undefined]
80
80
  });
@@ -0,0 +1,13 @@
1
+ /// <reference types="react" />
2
+ interface UseContainerSizeOptions {
3
+ debounceMs?: number;
4
+ }
5
+ export declare const useWidth: (options?: UseContainerSizeOptions) => {
6
+ ref: import("react").RefObject<HTMLDivElement | null>;
7
+ width: number | undefined;
8
+ };
9
+ export declare const useHeight: (options?: UseContainerSizeOptions) => {
10
+ height: number | undefined;
11
+ ref: import("react").RefObject<HTMLDivElement | null>;
12
+ };
13
+ export {};
@@ -7,7 +7,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
7
7
  // hooks/useContainerSize.ts
8
8
  import { debounce } from 'lodash-es';
9
9
  import { useCallback, useEffect, useRef, useState } from 'react';
10
- export var useContainerSize = function useContainerSize() {
10
+ export var useWidth = function useWidth() {
11
11
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12
12
  var _options$debounceMs = options.debounceMs,
13
13
  debounceMs = _options$debounceMs === void 0 ? 100 : _options$debounceMs;
@@ -32,12 +32,46 @@ export var useContainerSize = function useContainerSize() {
32
32
  if (resizeObserverRef.current) {
33
33
  var _updateSize$cancel;
34
34
  resizeObserverRef.current.disconnect();
35
- (_updateSize$cancel = updateSize.cancel) === null || _updateSize$cancel === void 0 || _updateSize$cancel.call(updateSize); // 清理防抖
35
+ (_updateSize$cancel = updateSize.cancel) === null || _updateSize$cancel === void 0 || _updateSize$cancel.call(updateSize);
36
36
  }
37
37
  };
38
38
  }, [updateSize]);
39
39
  return {
40
40
  ref: ref,
41
- size: size
41
+ width: size
42
+ };
43
+ };
44
+ export var useHeight = function useHeight() {
45
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
46
+ var _options$debounceMs2 = options.debounceMs,
47
+ debounceMs = _options$debounceMs2 === void 0 ? 100 : _options$debounceMs2;
48
+ var ref = useRef(null);
49
+ var _useState3 = useState(),
50
+ _useState4 = _slicedToArray(_useState3, 2),
51
+ size = _useState4[0],
52
+ setSize = _useState4[1];
53
+ var resizeObserverRef = useRef(null);
54
+ var updateSize = useCallback(debounce(function (entries) {
55
+ if (entries[0]) {
56
+ var height = entries[0].contentRect.height;
57
+ setSize(Math.floor(height));
58
+ }
59
+ }, debounceMs), [debounceMs]);
60
+ useEffect(function () {
61
+ var element = ref.current;
62
+ if (!element) return;
63
+ resizeObserverRef.current = new ResizeObserver(updateSize);
64
+ resizeObserverRef.current.observe(element);
65
+ return function () {
66
+ if (resizeObserverRef.current) {
67
+ var _updateSize$cancel2;
68
+ resizeObserverRef.current.disconnect();
69
+ (_updateSize$cancel2 = updateSize.cancel) === null || _updateSize$cancel2 === void 0 || _updateSize$cancel2.call(updateSize);
70
+ }
71
+ };
72
+ }, [updateSize]);
73
+ return {
74
+ height: size,
75
+ ref: ref
42
76
  };
43
77
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/editor",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "A powerful and extensible rich text editor built on Meta's Lexical framework, providing a modern editing experience with React integration.",
5
5
  "keywords": [
6
6
  "lobehub",
@@ -52,6 +52,7 @@
52
52
  "lodash-es": "^4.17.21",
53
53
  "lucide-react": "^0.536.0",
54
54
  "polished": "^4.3.1",
55
+ "re-resizable": "^6.11.2",
55
56
  "react-error-boundary": "^6.0.0",
56
57
  "react-layout-kit": "^2.0.0",
57
58
  "react-merge-refs": "^3.0.2",
@@ -1,9 +0,0 @@
1
- /// <reference types="react" />
2
- interface UseContainerSizeOptions {
3
- debounceMs?: number;
4
- }
5
- export declare const useContainerSize: (options?: UseContainerSizeOptions) => {
6
- ref: import("react").RefObject<HTMLElement | null>;
7
- size: number | undefined;
8
- };
9
- export {};