@lobehub/lobehub 2.0.0-next.216 → 2.0.0-next.218
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/changelog/v1.json +17 -0
- package/package.json +1 -1
- package/packages/builtin-tool-cloud-sandbox/src/ExecutionRuntime/index.ts +18 -31
- package/packages/builtin-tool-cloud-sandbox/src/types.ts +3 -3
- package/packages/utils/src/server/index.ts +0 -1
- package/src/app/[variants]/(main)/chat/profile/features/EditorCanvas/TypoBar.tsx +1 -11
- package/src/app/[variants]/(main)/group/profile/features/EditorCanvas/TypoBar.tsx +1 -11
- package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +44 -0
- package/src/app/[variants]/(main)/memory/features/EditableModal/index.tsx +8 -101
- package/src/features/ChatInput/InputEditor/index.tsx +1 -0
- package/src/features/ChatInput/TypoBar/index.tsx +0 -11
- package/src/features/CommandMenu/AskAIMenu.tsx +47 -14
- package/src/features/Conversation/ChatItem/components/MessageContent/index.tsx +11 -12
- package/src/features/EditorModal/EditorCanvas.tsx +81 -0
- package/src/features/EditorModal/TextareCanvas.tsx +28 -0
- package/src/features/{Conversation/ChatItem/components/MessageContent → EditorModal}/Typobar.tsx +0 -11
- package/src/features/EditorModal/index.tsx +51 -0
- package/src/features/ModelSwitchPanel/index.tsx +21 -1
- package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +1 -17
- package/src/server/routers/tools/market.ts +118 -102
- package/src/server/services/discover/index.ts +10 -5
- package/src/services/codeInterpreter.ts +12 -20
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +13 -86
- package/packages/utils/src/server/__tests__/geo.test.ts +0 -116
- package/packages/utils/src/server/geo.ts +0 -60
- package/src/app/[variants]/(main)/memory/features/EditableModal/Typobar.tsx +0 -150
- package/src/features/Conversation/ChatItem/components/MessageContent/EditableModal.tsx +0 -119
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.218](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.217...v2.0.0-next.218)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2026-01-05**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Update the sandbox export files & save files way.
|
|
12
|
+
|
|
13
|
+
#### 🐛 Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **misc**: Fix editor modal when Markdown rendering off.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### What's improved
|
|
23
|
+
|
|
24
|
+
- **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))
|
|
25
|
+
|
|
26
|
+
#### What's fixed
|
|
27
|
+
|
|
28
|
+
- **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))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
## [Version 2.0.0-next.217](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.216...v2.0.0-next.217)
|
|
39
|
+
|
|
40
|
+
<sup>Released on **2026-01-05**</sup>
|
|
41
|
+
|
|
42
|
+
#### ♻ Code Refactoring
|
|
43
|
+
|
|
44
|
+
- **utils**: Remove unused geo server utilities.
|
|
45
|
+
|
|
46
|
+
<br/>
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
50
|
+
|
|
51
|
+
#### Code refactoring
|
|
52
|
+
|
|
53
|
+
- **utils**: Remove unused geo server utilities, closes [#11243](https://github.com/lobehub/lobe-chat/issues/11243) ([ee474cc](https://github.com/lobehub/lobe-chat/commit/ee474cc))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
## [Version 2.0.0-next.216](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.215...v2.0.0-next.216)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2026-01-05**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"features": [
|
|
5
|
+
"Update the sandbox export files & save files way."
|
|
6
|
+
],
|
|
7
|
+
"fixes": [
|
|
8
|
+
"Fix editor modal when Markdown rendering off."
|
|
9
|
+
]
|
|
10
|
+
},
|
|
11
|
+
"date": "2026-01-05",
|
|
12
|
+
"version": "2.0.0-next.218"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"children": {},
|
|
16
|
+
"date": "2026-01-05",
|
|
17
|
+
"version": "2.0.0-next.217"
|
|
18
|
+
},
|
|
2
19
|
{
|
|
3
20
|
"children": {
|
|
4
21
|
"fixes": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.218",
|
|
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",
|
|
@@ -425,51 +425,38 @@ export class CloudSandboxExecutionRuntime {
|
|
|
425
425
|
|
|
426
426
|
/**
|
|
427
427
|
* Export a file from the sandbox to cloud storage
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
*
|
|
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
|
-
//
|
|
438
|
-
const
|
|
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
|
-
|
|
462
|
-
|
|
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:
|
|
467
|
-
success:
|
|
452
|
+
size: result.size,
|
|
453
|
+
success: result.success,
|
|
468
454
|
};
|
|
469
|
-
|
|
455
|
+
|
|
456
|
+
if (!result.success) {
|
|
470
457
|
return {
|
|
471
458
|
content: JSON.stringify({
|
|
472
|
-
error: result.
|
|
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: ${
|
|
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
|
-
/**
|
|
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 */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HotkeyEnum, getHotkeyById } from '@lobehub/editor';
|
|
2
2
|
import { FloatActions } from '@lobehub/editor/react';
|
|
3
|
-
import { type ChatInputActionsProps
|
|
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
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HotkeyEnum, getHotkeyById } from '@lobehub/editor';
|
|
2
2
|
import { FloatActions } from '@lobehub/editor/react';
|
|
3
|
-
import { type ChatInputActionsProps
|
|
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
|
|
|
@@ -5,11 +5,13 @@ import { Button, Flexbox, TextArea } from '@lobehub/ui';
|
|
|
5
5
|
import { createStaticStyles, cx } from 'antd-style';
|
|
6
6
|
import { Sparkles } from 'lucide-react';
|
|
7
7
|
import type { KeyboardEvent } from 'react';
|
|
8
|
+
import { useEffect, useRef } from 'react';
|
|
8
9
|
import { useTranslation } from 'react-i18next';
|
|
9
10
|
|
|
10
11
|
import { loginRequired } from '@/components/Error/loginRequiredNotification';
|
|
11
12
|
import { useGeminiChineseWarning } from '@/hooks/useGeminiChineseWarning';
|
|
12
13
|
import { useIsDark } from '@/hooks/useIsDark';
|
|
14
|
+
import { useQueryState } from '@/hooks/useQueryParam';
|
|
13
15
|
import { useImageStore } from '@/store/image';
|
|
14
16
|
import { createImageSelectors } from '@/store/image/selectors';
|
|
15
17
|
import { useGenerationConfigParam } from '@/store/image/slices/generationConfig/hooks';
|
|
@@ -49,6 +51,10 @@ const PromptInput = ({ showTitle = false }: PromptInputProps) => {
|
|
|
49
51
|
const isLogin = useUserStore(authSelectors.isLogin);
|
|
50
52
|
const checkGeminiChineseWarning = useGeminiChineseWarning();
|
|
51
53
|
|
|
54
|
+
// Read prompt from query parameter
|
|
55
|
+
const [promptParam, setPromptParam] = useQueryState('prompt');
|
|
56
|
+
const hasProcessedPrompt = useRef(false);
|
|
57
|
+
|
|
52
58
|
const handleGenerate = async () => {
|
|
53
59
|
if (!isLogin) {
|
|
54
60
|
loginRequired.redirect({ timeout: 2000 });
|
|
@@ -66,6 +72,44 @@ const PromptInput = ({ showTitle = false }: PromptInputProps) => {
|
|
|
66
72
|
await createImage();
|
|
67
73
|
};
|
|
68
74
|
|
|
75
|
+
// Auto-fill and auto-send when prompt query parameter is present
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (promptParam && !hasProcessedPrompt.current && isLogin) {
|
|
78
|
+
// Decode the prompt parameter
|
|
79
|
+
const decodedPrompt = decodeURIComponent(promptParam);
|
|
80
|
+
|
|
81
|
+
// Set the prompt value in the store
|
|
82
|
+
setValue(decodedPrompt);
|
|
83
|
+
|
|
84
|
+
// Mark as processed to avoid running this effect again
|
|
85
|
+
hasProcessedPrompt.current = true;
|
|
86
|
+
|
|
87
|
+
// Clear the query parameter
|
|
88
|
+
setPromptParam(null);
|
|
89
|
+
|
|
90
|
+
// Auto-trigger generation after a short delay to ensure state is updated
|
|
91
|
+
setTimeout(async () => {
|
|
92
|
+
const shouldContinue = await checkGeminiChineseWarning({
|
|
93
|
+
model: currentModel,
|
|
94
|
+
prompt: decodedPrompt,
|
|
95
|
+
scenario: 'image',
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (shouldContinue) {
|
|
99
|
+
await createImage();
|
|
100
|
+
}
|
|
101
|
+
}, 100);
|
|
102
|
+
}
|
|
103
|
+
}, [
|
|
104
|
+
promptParam,
|
|
105
|
+
isLogin,
|
|
106
|
+
setValue,
|
|
107
|
+
setPromptParam,
|
|
108
|
+
checkGeminiChineseWarning,
|
|
109
|
+
currentModel,
|
|
110
|
+
createImage,
|
|
111
|
+
]);
|
|
112
|
+
|
|
69
113
|
const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
|
|
70
114
|
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
71
115
|
e.preventDefault();
|
|
@@ -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 {
|
|
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
|
-
<
|
|
73
|
-
cancelText={t('cancel')}
|
|
74
|
-
closable={false}
|
|
75
|
-
confirmLoading={confirmLoading}
|
|
76
|
-
destroyOnHidden
|
|
77
|
-
okText={t('ok')}
|
|
22
|
+
<EditorModal
|
|
78
23
|
onCancel={clearEditingMemory}
|
|
79
|
-
|
|
80
|
-
if (!
|
|
81
|
-
|
|
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
|
-
|
|
88
|
-
|
|
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
|
|
|
@@ -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
|
);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_AVATAR, DEFAULT_INBOX_AVATAR } from '@lobechat/const';
|
|
2
2
|
import { Avatar } from '@lobehub/ui';
|
|
3
|
+
import { GroupBotSquareIcon } from '@lobehub/ui/icons';
|
|
3
4
|
import { Command } from 'cmdk';
|
|
4
|
-
import { Image } from 'lucide-react';
|
|
5
|
+
import { Bot, Image } from 'lucide-react';
|
|
5
6
|
import { memo } from 'react';
|
|
6
7
|
import { useTranslation } from 'react-i18next';
|
|
7
8
|
import { useNavigate } from 'react-router-dom';
|
|
@@ -10,11 +11,12 @@ import { useHomeStore } from '@/store/home';
|
|
|
10
11
|
import { homeAgentListSelectors } from '@/store/home/selectors';
|
|
11
12
|
|
|
12
13
|
import { useCommandMenuContext } from './CommandMenuContext';
|
|
14
|
+
import { CommandItem } from './components';
|
|
13
15
|
import { styles } from './styles';
|
|
14
16
|
import { useCommandMenu } from './useCommandMenu';
|
|
15
17
|
|
|
16
18
|
const AskAIMenu = memo(() => {
|
|
17
|
-
const { t } = useTranslation('common');
|
|
19
|
+
const { t } = useTranslation(['common', 'chat', 'home']);
|
|
18
20
|
const navigate = useNavigate();
|
|
19
21
|
const { handleAskLobeAI, handleAIPainting, closeCommandMenu } = useCommandMenu();
|
|
20
22
|
const { search } = useCommandMenuContext();
|
|
@@ -27,6 +29,24 @@ const AskAIMenu = memo(() => {
|
|
|
27
29
|
? t('cmdk.askAIHeading', { query: `"${search.trim()}"` })
|
|
28
30
|
: t('cmdk.askAIHeadingEmpty');
|
|
29
31
|
|
|
32
|
+
const handleAgentBuilder = () => {
|
|
33
|
+
const trimmedSearch = search.trim();
|
|
34
|
+
closeCommandMenu(); // Close immediately
|
|
35
|
+
if (trimmedSearch) {
|
|
36
|
+
// Use sendAsAgent to create a blank agent and open agent builder
|
|
37
|
+
useHomeStore.getState().sendAsAgent(trimmedSearch);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const handleGroupBuilder = () => {
|
|
42
|
+
const trimmedSearch = search.trim();
|
|
43
|
+
closeCommandMenu(); // Close immediately
|
|
44
|
+
if (trimmedSearch) {
|
|
45
|
+
// Use sendAsGroup to create a blank group and open group builder
|
|
46
|
+
useHomeStore.getState().sendAsGroup(trimmedSearch);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
30
50
|
const handleAgentSelect = (agentId: string) => {
|
|
31
51
|
if (search.trim()) {
|
|
32
52
|
const message = encodeURIComponent(search.trim());
|
|
@@ -45,6 +65,18 @@ const AskAIMenu = memo(() => {
|
|
|
45
65
|
<div className={styles.itemLabel}>Lobe AI</div>
|
|
46
66
|
</div>
|
|
47
67
|
</Command.Item>
|
|
68
|
+
<Command.Item onSelect={handleAgentBuilder} value="agent-builder">
|
|
69
|
+
<Bot className={styles.icon} />
|
|
70
|
+
<div className={styles.itemContent}>
|
|
71
|
+
<div className={styles.itemLabel}>{t('agentBuilder.title', { ns: 'chat' })}</div>
|
|
72
|
+
</div>
|
|
73
|
+
</Command.Item>
|
|
74
|
+
<Command.Item onSelect={handleGroupBuilder} value="group-builder">
|
|
75
|
+
<GroupBotSquareIcon className={styles.icon} />
|
|
76
|
+
<div className={styles.itemContent}>
|
|
77
|
+
<div className={styles.itemLabel}>{t('starter.createGroup', { ns: 'home' })}</div>
|
|
78
|
+
</div>
|
|
79
|
+
</Command.Item>
|
|
48
80
|
<Command.Item onSelect={handleAIPainting} value="ai-painting">
|
|
49
81
|
<Image className={styles.icon} />
|
|
50
82
|
<div className={styles.itemContent}>
|
|
@@ -53,21 +85,22 @@ const AskAIMenu = memo(() => {
|
|
|
53
85
|
</Command.Item>
|
|
54
86
|
|
|
55
87
|
{agents.map((agent) => (
|
|
56
|
-
<
|
|
88
|
+
<CommandItem
|
|
89
|
+
icon={
|
|
90
|
+
<Avatar
|
|
91
|
+
avatar={typeof agent.avatar === 'string' ? agent.avatar : DEFAULT_AVATAR}
|
|
92
|
+
emojiScaleWithBackground
|
|
93
|
+
shape="square"
|
|
94
|
+
size={18}
|
|
95
|
+
/>
|
|
96
|
+
}
|
|
57
97
|
key={agent.id}
|
|
58
98
|
onSelect={() => handleAgentSelect(agent.id)}
|
|
99
|
+
title={agent.title || t('defaultAgent')}
|
|
100
|
+
trailingLabel={t('cmdk.search.agent')}
|
|
59
101
|
value={`agent-${agent.id}`}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
avatar={typeof agent.avatar === 'string' ? agent.avatar : DEFAULT_AVATAR}
|
|
63
|
-
emojiScaleWithBackground
|
|
64
|
-
shape="square"
|
|
65
|
-
size={18}
|
|
66
|
-
/>
|
|
67
|
-
<div className={styles.itemContent}>
|
|
68
|
-
<div className={styles.itemLabel}>{agent.title || t('defaultAgent')}</div>
|
|
69
|
-
</div>
|
|
70
|
-
</Command.Item>
|
|
102
|
+
variant="detailed"
|
|
103
|
+
/>
|
|
71
104
|
))}
|
|
72
105
|
</Command.Group>
|
|
73
106
|
);
|
|
@@ -7,7 +7,10 @@ import { useConversationStore } from '@/features/Conversation/store';
|
|
|
7
7
|
|
|
8
8
|
import { type ChatItemProps } from '../../type';
|
|
9
9
|
|
|
10
|
-
const
|
|
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
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
)}
|