@lobehub/lobehub 2.0.0-next.217 → 2.0.0-next.219

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 (35) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/changelog/v1.json +21 -0
  3. package/package.json +2 -2
  4. package/packages/builtin-tool-cloud-sandbox/src/ExecutionRuntime/index.ts +18 -31
  5. package/packages/builtin-tool-cloud-sandbox/src/types.ts +3 -3
  6. package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/List/Item/Actions.tsx +1 -1
  7. package/src/app/[variants]/(main)/chat/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/Actions.tsx +1 -1
  8. package/src/app/[variants]/(main)/chat/profile/features/EditorCanvas/TypoBar.tsx +1 -11
  9. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/List/Item/Actions.tsx +1 -1
  10. package/src/app/[variants]/(main)/group/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/Actions.tsx +1 -1
  11. package/src/app/[variants]/(main)/group/profile/features/EditorCanvas/TypoBar.tsx +1 -11
  12. package/src/app/[variants]/(main)/home/_layout/Body/Agent/List/Item/Actions.tsx +1 -1
  13. package/src/app/[variants]/(main)/home/_layout/Body/Project/List/Actions.tsx +1 -1
  14. package/src/app/[variants]/(main)/home/_layout/Footer/index.tsx +3 -9
  15. package/src/app/[variants]/(main)/home/_layout/Header/components/AddButton.tsx +1 -1
  16. package/src/app/[variants]/(main)/memory/features/EditableModal/index.tsx +8 -101
  17. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/Actions.tsx +1 -1
  18. package/src/app/[variants]/(main)/resource/(home)/_layout/Body/LibraryList/List/Item/Actions.tsx +1 -1
  19. package/src/features/ChatInput/InputEditor/index.tsx +1 -0
  20. package/src/features/ChatInput/TypoBar/index.tsx +0 -11
  21. package/src/features/Conversation/ChatItem/components/MessageContent/index.tsx +11 -12
  22. package/src/features/EditorModal/EditorCanvas.tsx +81 -0
  23. package/src/features/EditorModal/TextareCanvas.tsx +28 -0
  24. package/src/{app/[variants]/(main)/memory/features/EditableModal → features/EditorModal}/Typobar.tsx +0 -11
  25. package/src/features/EditorModal/index.tsx +51 -0
  26. package/src/features/PageEditor/Copilot/TopicSelector/Actions.tsx +1 -1
  27. package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +1 -17
  28. package/src/features/ResourceManager/components/Explorer/ItemDropdown/DropdownMenu.tsx +1 -1
  29. package/src/features/User/UserPanel/ThemeButton.tsx +1 -1
  30. package/src/server/routers/tools/market.ts +118 -102
  31. package/src/server/services/discover/index.ts +10 -5
  32. package/src/services/codeInterpreter.ts +12 -20
  33. package/src/store/chat/slices/plugin/actions/pluginTypes.ts +13 -86
  34. package/src/features/Conversation/ChatItem/components/MessageContent/EditableModal.tsx +0 -119
  35. package/src/features/Conversation/ChatItem/components/MessageContent/Typobar.tsx +0 -150
package/CHANGELOG.md CHANGED
@@ -2,6 +2,64 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.219](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.218...v2.0.0-next.219)
6
+
7
+ <sup>Released on **2026-01-05**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Resolve BaseUI dropdown compatibility issue.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Resolve BaseUI dropdown compatibility issue, closes [#11248](https://github.com/lobehub/lobe-chat/issues/11248) ([065bfec](https://github.com/lobehub/lobe-chat/commit/065bfec))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ## [Version 2.0.0-next.218](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.217...v2.0.0-next.218)
31
+
32
+ <sup>Released on **2026-01-05**</sup>
33
+
34
+ #### ✨ Features
35
+
36
+ - **misc**: Update the sandbox export files & save files way.
37
+
38
+ #### 🐛 Bug Fixes
39
+
40
+ - **misc**: Fix editor modal when Markdown rendering off.
41
+
42
+ <br/>
43
+
44
+ <details>
45
+ <summary><kbd>Improvements and Fixes</kbd></summary>
46
+
47
+ #### What's improved
48
+
49
+ - **misc**: Update the sandbox export files & save files way, closes [#11249](https://github.com/lobehub/lobe-chat/issues/11249) ([039b0a1](https://github.com/lobehub/lobe-chat/commit/039b0a1))
50
+
51
+ #### What's fixed
52
+
53
+ - **misc**: Fix editor modal when Markdown rendering off, closes [#11251](https://github.com/lobehub/lobe-chat/issues/11251) ([eb86d3b](https://github.com/lobehub/lobe-chat/commit/eb86d3b))
54
+
55
+ </details>
56
+
57
+ <div align="right">
58
+
59
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
60
+
61
+ </div>
62
+
5
63
  ## [Version 2.0.0-next.217](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.216...v2.0.0-next.217)
6
64
 
7
65
  <sup>Released on **2026-01-05**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,25 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Resolve BaseUI dropdown compatibility issue."
6
+ ]
7
+ },
8
+ "date": "2026-01-05",
9
+ "version": "2.0.0-next.219"
10
+ },
11
+ {
12
+ "children": {
13
+ "features": [
14
+ "Update the sandbox export files & save files way."
15
+ ],
16
+ "fixes": [
17
+ "Fix editor modal when Markdown rendering off."
18
+ ]
19
+ },
20
+ "date": "2026-01-05",
21
+ "version": "2.0.0-next.218"
22
+ },
2
23
  {
3
24
  "children": {},
4
25
  "date": "2026-01-05",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.217",
3
+ "version": "2.0.0-next.219",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -205,7 +205,7 @@
205
205
  "@lobehub/icons": "^4.0.2",
206
206
  "@lobehub/market-sdk": "^0.25.1",
207
207
  "@lobehub/tts": "^4.0.2",
208
- "@lobehub/ui": "^4.9.0",
208
+ "@lobehub/ui": "^4.9.3",
209
209
  "@modelcontextprotocol/sdk": "^1.25.1",
210
210
  "@neondatabase/serverless": "^1.0.2",
211
211
  "@next/third-parties": "^16.1.1",
@@ -425,51 +425,38 @@ export class CloudSandboxExecutionRuntime {
425
425
 
426
426
  /**
427
427
  * Export a file from the sandbox to cloud storage
428
- * 1. Get a pre-signed upload URL from our server
429
- * 2. Call the sandbox to upload the file to that URL
430
- * 3. Return the download URL to the user
428
+ * Uses a single tRPC call that handles:
429
+ * 1. Generate pre-signed upload URL
430
+ * 2. Call sandbox to upload file
431
+ * 3. Create persistent file record
432
+ * 4. Return permanent /f/:id URL
431
433
  */
432
434
  async exportFile(args: ExportFileParams): Promise<BuiltinServerRuntimeOutput> {
433
435
  try {
434
436
  // Extract filename from path
435
437
  const filename = args.path.split('/').pop() || 'exported_file';
436
438
 
437
- // Step 1: Get pre-signed upload URL from our server
438
- const uploadUrlResult = await codeInterpreterService.getExportFileUploadUrl(
439
+ // Single call that handles everything: upload URL generation, sandbox upload, and file record creation
440
+ const result = await codeInterpreterService.exportAndUploadFile(
441
+ args.path,
439
442
  filename,
440
443
  this.context.topicId,
441
444
  );
442
445
 
443
- if (!uploadUrlResult.success) {
444
- throw new Error(uploadUrlResult.error?.message || 'Failed to get upload URL');
445
- }
446
-
447
- // Step 2: Call the sandbox's exportFile tool with the upload URL
448
- // The sandbox will read the file and upload it to the pre-signed URL
449
- const result = await this.callTool('exportFile', {
450
- path: args.path,
451
- uploadUrl: uploadUrlResult.uploadUrl,
452
- });
453
-
454
- // Check if the sandbox upload was successful
455
- const uploadSuccess = result.success && result.result?.success !== false;
456
- const fileSize = result.result?.size;
457
- const mimeType = result.result?.mimeType;
458
- const fileContent = result.result?.content;
459
-
460
446
  const state: ExportFileState = {
461
- content: fileContent,
462
- downloadUrl: uploadSuccess ? uploadUrlResult.downloadUrl : '',
463
- filename,
464
- mimeType,
447
+ downloadUrl: result.success && result.url ? result.url : '',
448
+ fileId: result.fileId,
449
+ filename: result.filename,
450
+ mimeType: result.mimeType,
465
451
  path: args.path,
466
- size: fileSize,
467
- success: uploadSuccess,
452
+ size: result.size,
453
+ success: result.success,
468
454
  };
469
- if (!uploadSuccess) {
455
+
456
+ if (!result.success) {
470
457
  return {
471
458
  content: JSON.stringify({
472
- error: result.result?.error || 'Failed to upload file from sandbox',
459
+ error: result.error?.message || 'Failed to export file from sandbox',
473
460
  filename,
474
461
  success: false,
475
462
  }),
@@ -479,7 +466,7 @@ export class CloudSandboxExecutionRuntime {
479
466
  }
480
467
 
481
468
  return {
482
- content: `File exported successfully.\n\nFilename: ${filename}\nDownload URL: ${uploadUrlResult.downloadUrl}`,
469
+ content: `File exported successfully.\n\nFilename: ${filename}\nDownload URL: ${result.url}`,
483
470
  state,
484
471
  success: true,
485
472
  };
@@ -90,10 +90,10 @@ export interface GlobFilesState {
90
90
  }
91
91
 
92
92
  export interface ExportFileState {
93
- /** File content for text files (only when mimeType is text-like and size <= 1MB) */
94
- content?: string;
95
- /** The download URL for the exported file */
93
+ /** The download URL for the exported file (permanent /f/:id URL) */
96
94
  downloadUrl: string;
95
+ /** The file ID in database (returned from server) */
96
+ fileId?: string;
97
97
  /** The exported file name */
98
98
  filename: string;
99
99
  /** The MIME type of the file */
@@ -8,7 +8,7 @@ interface ActionProps {
8
8
 
9
9
  const Actions = memo<ActionProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -8,7 +8,7 @@ interface ActionProps {
8
8
 
9
9
  const Actions = memo<ActionProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -1,6 +1,6 @@
1
1
  import { HotkeyEnum, getHotkeyById } from '@lobehub/editor';
2
2
  import { FloatActions } from '@lobehub/editor/react';
3
- import { type ChatInputActionsProps, CodeLanguageSelect } from '@lobehub/editor/react';
3
+ import { type ChatInputActionsProps } from '@lobehub/editor/react';
4
4
  import {
5
5
  BoldIcon,
6
6
  CodeXmlIcon,
@@ -116,16 +116,6 @@ const TypoBar = memo(() => {
116
116
  label: t('typobar.codeblock'),
117
117
  onClick: editorState.codeblock,
118
118
  },
119
- editorState.isCodeblock && {
120
- children: (
121
- <CodeLanguageSelect
122
- onSelect={(value) => editorState.updateCodeblockLang(value)}
123
- value={editorState.codeblockLang}
124
- />
125
- ),
126
- disabled: !editorState.isCodeblock,
127
- key: 'codeblockLang',
128
- },
129
119
  ].filter(Boolean) as ChatInputActionsProps['items'];
130
120
  }, [editorState, t]);
131
121
 
@@ -8,7 +8,7 @@ interface ActionProps {
8
8
 
9
9
  const Actions = memo<ActionProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -8,7 +8,7 @@ interface ActionProps {
8
8
 
9
9
  const Actions = memo<ActionProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -1,6 +1,6 @@
1
1
  import { HotkeyEnum, getHotkeyById } from '@lobehub/editor';
2
2
  import { FloatActions } from '@lobehub/editor/react';
3
- import { type ChatInputActionsProps, CodeLanguageSelect } from '@lobehub/editor/react';
3
+ import { type ChatInputActionsProps } from '@lobehub/editor/react';
4
4
  import {
5
5
  BoldIcon,
6
6
  CodeXmlIcon,
@@ -116,16 +116,6 @@ const TypoBar = memo(() => {
116
116
  label: t('typobar.codeblock'),
117
117
  onClick: editorState.codeblock,
118
118
  },
119
- editorState.isCodeblock && {
120
- children: (
121
- <CodeLanguageSelect
122
- onSelect={(value) => editorState.updateCodeblockLang(value)}
123
- value={editorState.codeblockLang}
124
- />
125
- ),
126
- disabled: !editorState.isCodeblock,
127
- key: 'codeblockLang',
128
- },
129
119
  ].filter(Boolean) as ChatInputActionsProps['items'];
130
120
  }, [editorState, t]);
131
121
 
@@ -8,7 +8,7 @@ interface ActionProps {
8
8
 
9
9
  const Actions = memo<ActionProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -8,7 +8,7 @@ interface ActionsProps {
8
8
 
9
9
  const Actions = memo<ActionsProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { BRANDING_EMAIL, SOCIAL_URL } from '@lobechat/business-const';
4
- import { ActionIcon, Dropdown, Icon, type MenuProps } from '@lobehub/ui';
4
+ import { ActionIcon, DropdownMenu, Icon, type MenuProps } from '@lobehub/ui';
5
5
  import { Flexbox } from '@lobehub/ui';
6
6
  import { DiscordIcon } from '@lobehub/ui/icons';
7
7
  import { Book, CircleHelp, Feather, FileClockIcon, FlaskConical, Github, Mail } from 'lucide-react';
@@ -103,15 +103,9 @@ const Footer = memo(() => {
103
103
  <>
104
104
  <Flexbox align={'center'} gap={2} horizontal justify={'space-between'} padding={8}>
105
105
  <Flexbox align={'center'} flex={1} gap={2} horizontal>
106
- <Dropdown
107
- menu={{
108
- items: helpMenuItems,
109
- }}
110
- placement="topLeft"
111
- trigger={['click']}
112
- >
106
+ <DropdownMenu items={helpMenuItems} nativeButton={false} placement="topLeft">
113
107
  <ActionIcon aria-label={t('userPanel.help')} icon={CircleHelp} size={16} />
114
- </Dropdown>
108
+ </DropdownMenu>
115
109
  {!hideGitHub && (
116
110
  <a aria-label={'GitHub'} href={GITHUB} rel="noopener noreferrer" target={'_blank'}>
117
111
  <ActionIcon icon={Github} size={16} title={'GitHub'} />
@@ -44,7 +44,7 @@ const AddButton = memo(() => {
44
44
  size={DESKTOP_HEADER_ICON_SIZE}
45
45
  title={tChat('newAgent')}
46
46
  />
47
- <DropdownMenu items={dropdownItems}>
47
+ <DropdownMenu items={dropdownItems} nativeButton={false}>
48
48
  <ActionIcon
49
49
  color={cssVar.colorTextQuaternary}
50
50
  icon={ChevronDownIcon}
@@ -1,66 +1,16 @@
1
- import {
2
- ReactCodePlugin,
3
- ReactCodemirrorPlugin,
4
- ReactHRPlugin,
5
- ReactLinkHighlightPlugin,
6
- ReactListPlugin,
7
- ReactMathPlugin,
8
- ReactTablePlugin,
9
- } from '@lobehub/editor';
10
- import { Editor, useEditor } from '@lobehub/editor/react';
11
- import { Flexbox, Modal } from '@lobehub/ui';
12
- import { memo, useMemo, useState } from 'react';
13
- import { useTranslation } from 'react-i18next';
1
+ import { memo } from 'react';
14
2
 
15
- import { useUserStore } from '@/store/user';
16
- import { labPreferSelectors } from '@/store/user/slices/preference/selectors';
3
+ import { EditorModal } from '@/features/EditorModal';
17
4
  import { useUserMemoryStore } from '@/store/userMemory';
18
5
  import { LayersEnum } from '@/types/userMemory';
19
6
 
20
- import TypoBar from './Typobar';
21
-
22
7
  const EditableModal = memo(() => {
23
- const { t } = useTranslation('common');
24
- const editor = useEditor();
25
- const [confirmLoading, setConfirmLoading] = useState(false);
26
8
  const editingMemoryId = useUserMemoryStore((s) => s.editingMemoryId);
27
9
  const editingMemoryContent = useUserMemoryStore((s) => s.editingMemoryContent);
28
10
  const editingMemoryLayer = useUserMemoryStore((s) => s.editingMemoryLayer);
29
11
  const clearEditingMemory = useUserMemoryStore((s) => s.clearEditingMemory);
30
12
  const updateMemory = useUserMemoryStore((s) => s.updateMemory);
31
13
 
32
- const enableRichRender = useUserStore(labPreferSelectors.enableInputMarkdown);
33
-
34
- const richRenderProps = useMemo(
35
- () =>
36
- !enableRichRender
37
- ? {
38
- enablePasteMarkdown: false,
39
- markdownOption: {
40
- bold: false,
41
- code: false,
42
- header: false,
43
- italic: false,
44
- quote: false,
45
- strikethrough: false,
46
- underline: false,
47
- underlineStrikethrough: false,
48
- },
49
- }
50
- : {
51
- plugins: [
52
- ReactListPlugin,
53
- ReactCodePlugin,
54
- ReactCodemirrorPlugin,
55
- ReactHRPlugin,
56
- ReactLinkHighlightPlugin,
57
- ReactTablePlugin,
58
- ReactMathPlugin,
59
- ],
60
- },
61
- [enableRichRender],
62
- );
63
-
64
14
  const layerMap = {
65
15
  context: LayersEnum.Context,
66
16
  experience: LayersEnum.Experience,
@@ -69,58 +19,15 @@ const EditableModal = memo(() => {
69
19
  };
70
20
 
71
21
  return (
72
- <Modal
73
- cancelText={t('cancel')}
74
- closable={false}
75
- confirmLoading={confirmLoading}
76
- destroyOnHidden
77
- okText={t('ok')}
22
+ <EditorModal
78
23
  onCancel={clearEditingMemory}
79
- onOk={async () => {
80
- if (!editor || !editingMemoryId || !editingMemoryLayer) return;
81
- setConfirmLoading(true);
82
- const newValue = editor.getDocument('markdown') as unknown as string;
83
- await updateMemory(editingMemoryId, newValue, layerMap[editingMemoryLayer]);
84
- setConfirmLoading(false);
24
+ onConfirm={async (value) => {
25
+ if (!editingMemoryId || !editingMemoryLayer) return;
26
+ await updateMemory(editingMemoryId, value, layerMap[editingMemoryLayer]);
85
27
  }}
86
28
  open={!!editingMemoryId}
87
- styles={{
88
- body: {
89
- overflow: 'hidden',
90
- padding: 0,
91
- },
92
- }}
93
- title={null}
94
- width={'min(90vw, 960px)'}
95
- >
96
- <TypoBar editor={editor} />
97
- <Flexbox
98
- onClick={() => {
99
- editor.focus();
100
- }}
101
- paddingBlock={16}
102
- paddingInline={48}
103
- style={{ cursor: 'text', maxHeight: '80vh', minHeight: '50vh', overflowY: 'auto' }}
104
- >
105
- <Editor
106
- autoFocus
107
- content={''}
108
- editor={editor}
109
- onInit={(editor) => {
110
- if (!editor) return;
111
- try {
112
- editor?.setDocument('markdown', editingMemoryContent);
113
- } catch {}
114
- }}
115
- style={{
116
- paddingBottom: 120,
117
- }}
118
- type={'text'}
119
- variant={'chat'}
120
- {...richRenderProps}
121
- />
122
- </Flexbox>
123
- </Modal>
29
+ value={editingMemoryContent}
30
+ />
124
31
  );
125
32
  });
126
33
 
@@ -8,7 +8,7 @@ interface ActionProps {
8
8
 
9
9
  const Actions = memo<ActionProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -8,7 +8,7 @@ interface ActionProps {
8
8
 
9
9
  const Actions = memo<ActionProps>(({ dropdownMenu }) => {
10
10
  return (
11
- <DropdownMenu items={dropdownMenu}>
11
+ <DropdownMenu items={dropdownMenu} nativeButton={false}>
12
12
  <ActionIcon icon={MoreHorizontalIcon} size={'small'} />
13
13
  </DropdownMenu>
14
14
  );
@@ -99,6 +99,7 @@ const InputEditor = memo<{ defaultRows?: number }>(({ defaultRows = 2 }) => {
99
99
  underline: false,
100
100
  underlineStrikethrough: false,
101
101
  },
102
+ plugins: [ReactCodemirrorPlugin],
102
103
  }
103
104
  : {
104
105
  plugins: [
@@ -4,7 +4,6 @@ import {
4
4
  ChatInputActionBar,
5
5
  ChatInputActions,
6
6
  type ChatInputActionsProps,
7
- CodeLanguageSelect,
8
7
  } from '@lobehub/editor/react';
9
8
  import { cssVar } from 'antd-style';
10
9
  import {
@@ -122,16 +121,6 @@ const TypoBar = memo(() => {
122
121
  label: t('typobar.codeblock'),
123
122
  onClick: editorState.codeblock,
124
123
  },
125
- editorState.isCodeblock && {
126
- children: (
127
- <CodeLanguageSelect
128
- onSelect={(value) => editorState.updateCodeblockLang(value)}
129
- value={editorState.codeblockLang}
130
- />
131
- ),
132
- disabled: !editorState.isCodeblock,
133
- key: 'codeblockLang',
134
- },
135
124
  ].filter(Boolean) as ChatInputActionsProps['items'],
136
125
  [editorState],
137
126
  );
@@ -7,7 +7,10 @@ import { useConversationStore } from '@/features/Conversation/store';
7
7
 
8
8
  import { type ChatItemProps } from '../../type';
9
9
 
10
- const EditableModal = dynamic(() => import('./EditableModal'), { ssr: false });
10
+ const EditorModal = dynamic(
11
+ () => import('@/features/EditorModal').then((mode) => mode.EditorModal),
12
+ { ssr: false },
13
+ );
11
14
 
12
15
  export const MSG_CONTENT_CLASSNAME = 'msg_content_flag';
13
16
 
@@ -60,13 +63,6 @@ const MessageContent = memo<MessageContentProps>(
60
63
  s.updateMessageContent,
61
64
  ]);
62
65
 
63
- const onChange = useCallback(
64
- (value: string) => {
65
- updateMessageContent(id, value);
66
- },
67
- [id, updateMessageContent],
68
- );
69
-
70
66
  const onEditingChange = useCallback(
71
67
  (edit: boolean) => toggleMessageEditing(id, edit),
72
68
  [id, toggleMessageEditing],
@@ -90,10 +86,13 @@ const MessageContent = memo<MessageContentProps>(
90
86
  </Flexbox>
91
87
  <Suspense fallback={null}>
92
88
  {editing && (
93
- <EditableModal
94
- editing={editing}
95
- onChange={onChange}
96
- onEditingChange={onEditingChange}
89
+ <EditorModal
90
+ onCancel={() => onEditingChange(false)}
91
+ onConfirm={async (value) => {
92
+ await updateMessageContent(id, value);
93
+ onEditingChange(false);
94
+ }}
95
+ open={editing}
97
96
  value={message ? String(message) : ''}
98
97
  />
99
98
  )}
@@ -0,0 +1,81 @@
1
+ import {
2
+ ReactCodePlugin,
3
+ ReactCodemirrorPlugin,
4
+ ReactHRPlugin,
5
+ ReactLinkPlugin,
6
+ ReactListPlugin,
7
+ ReactMathPlugin,
8
+ ReactTablePlugin,
9
+ } from '@lobehub/editor';
10
+ import { Editor, useEditor } from '@lobehub/editor/react';
11
+ import { Flexbox } from '@lobehub/ui';
12
+ import { FC } from 'react';
13
+
14
+ import TypoBar from './Typobar';
15
+
16
+ interface EditorCanvasProps {
17
+ onChange?: (value: string) => void;
18
+ value?: string;
19
+ }
20
+
21
+ const EditorCanvas: FC<EditorCanvasProps> = ({ value, onChange }) => {
22
+ const editor = useEditor();
23
+ return (
24
+ <>
25
+ <TypoBar editor={editor} />
26
+ <Flexbox
27
+ onClick={() => {
28
+ editor?.focus();
29
+ }}
30
+ padding={16}
31
+ style={{ cursor: 'text', maxHeight: '80vh', minHeight: '50vh', overflowY: 'auto' }}
32
+ >
33
+ <div
34
+ onClick={(e) => {
35
+ e.stopPropagation();
36
+ e.preventDefault();
37
+ }}
38
+ >
39
+ <Editor
40
+ autoFocus
41
+ content={''}
42
+ editor={editor}
43
+ onInit={(editor) => {
44
+ if (!editor || !value) return;
45
+ try {
46
+ editor?.setDocument('markdown', value);
47
+ } catch (e) {
48
+ console.error('setDocument error:', e);
49
+ }
50
+ }}
51
+ onTextChange={(editor) => {
52
+ try {
53
+ const newValue = editor.getDocument('markdown') as unknown as string;
54
+ onChange?.(newValue);
55
+ } catch (e) {
56
+ console.error('getDocument error:', e);
57
+ onChange?.('');
58
+ }
59
+ }}
60
+ plugins={[
61
+ ReactListPlugin,
62
+ ReactCodePlugin,
63
+ ReactCodemirrorPlugin,
64
+ ReactHRPlugin,
65
+ ReactLinkPlugin,
66
+ ReactTablePlugin,
67
+ ReactMathPlugin,
68
+ ]}
69
+ style={{
70
+ paddingBottom: 120,
71
+ }}
72
+ type={'text'}
73
+ variant={'chat'}
74
+ />
75
+ </div>
76
+ </Flexbox>
77
+ </>
78
+ );
79
+ };
80
+
81
+ export default EditorCanvas;