@lobehub/chat 0.118.10 → 0.119.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.
- package/CHANGELOG.md +44 -0
- package/locales/de-DE/chat.json +10 -0
- package/locales/de-DE/common.json +1 -0
- package/locales/de-DE/error.json +4 -4
- package/locales/en-US/chat.json +10 -0
- package/locales/en-US/common.json +1 -0
- package/locales/en-US/error.json +1 -1
- package/locales/es-ES/chat.json +10 -0
- package/locales/es-ES/common.json +1 -0
- package/locales/fr-FR/chat.json +10 -0
- package/locales/fr-FR/common.json +1 -0
- package/locales/fr-FR/error.json +1 -2
- package/locales/it-IT/chat.json +10 -0
- package/locales/it-IT/common.json +1 -0
- package/locales/it-IT/error.json +5 -5
- package/locales/ja-JP/chat.json +10 -0
- package/locales/ja-JP/common.json +1 -0
- package/locales/ko-KR/chat.json +10 -0
- package/locales/ko-KR/common.json +1 -0
- package/locales/ko-KR/error.json +4 -4
- package/locales/nl-NL/chat.json +10 -0
- package/locales/nl-NL/common.json +1 -0
- package/locales/pl-PL/chat.json +10 -0
- package/locales/pl-PL/common.json +1 -0
- package/locales/pl-PL/error.json +2 -3
- package/locales/pt-BR/chat.json +10 -0
- package/locales/pt-BR/common.json +1 -0
- package/locales/ru-RU/chat.json +10 -0
- package/locales/ru-RU/common.json +1 -0
- package/locales/ru-RU/error.json +3 -3
- package/locales/tr-TR/chat.json +10 -0
- package/locales/tr-TR/common.json +1 -0
- package/locales/vi-VN/chat.json +10 -0
- package/locales/vi-VN/common.json +1 -0
- package/locales/vi-VN/error.json +1 -1
- package/locales/zh-CN/chat.json +10 -0
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-TW/chat.json +10 -0
- package/locales/zh-TW/common.json +1 -0
- package/package.json +2 -2
- package/src/app/chat/(desktop)/features/Conversation.tsx +17 -0
- package/src/app/chat/(desktop)/features/HotKeys.tsx +44 -0
- package/src/app/chat/(desktop)/index.tsx +2 -3
- package/src/app/chat/features/ChatHeader/ShareButton/Preview.tsx +1 -1
- package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +24 -3
- package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +43 -2
- package/src/database/models/__tests__/message.test.ts +48 -0
- package/src/database/models/__tests__/topic.test.ts +135 -0
- package/src/database/models/message.ts +35 -0
- package/src/database/models/session.ts +11 -1
- package/src/database/models/topic.ts +34 -2
- package/src/features/ChatInput/ActionBar/Clear.tsx +0 -6
- package/src/features/Conversation/Actions/Assistant.tsx +16 -5
- package/src/features/Conversation/Actions/Error.tsx +2 -2
- package/src/features/Conversation/Actions/Fallback.tsx +4 -3
- package/src/features/Conversation/Actions/Function.tsx +3 -3
- package/src/features/Conversation/Actions/User.tsx +3 -3
- package/src/features/Conversation/Actions/customAction.ts +2 -4
- package/src/features/Conversation/Actions/index.ts +50 -45
- package/src/features/Conversation/Error/InvalidAccess.tsx +1 -1
- package/src/features/Conversation/Error/OpenAPIKey.tsx +1 -1
- package/src/features/Conversation/Error/OpenAiBizError.tsx +1 -1
- package/src/features/Conversation/Error/Plugin/PluginError.tsx +1 -1
- package/src/features/Conversation/Error/Plugin/PluginSettings.tsx +1 -1
- package/src/features/Conversation/Error/index.tsx +2 -2
- package/src/features/Conversation/Extras/Assistant.tsx +32 -29
- package/src/features/Conversation/Extras/User.tsx +2 -1
- package/src/features/Conversation/Extras/index.ts +4 -4
- package/src/features/Conversation/Messages/index.ts +3 -3
- package/src/features/Conversation/components/ChatItem/ActionsBar.tsx +58 -0
- package/src/features/Conversation/components/ChatItem/index.tsx +126 -0
- package/src/features/Conversation/components/ChatList/HistoryDivider.tsx +6 -3
- package/src/features/Conversation/components/ChatList/index.tsx +35 -79
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +43 -31
- package/src/features/Conversation/hooks/useInitConversation.ts +4 -1
- package/src/features/Conversation/index.tsx +4 -3
- package/src/features/Conversation/types/index.tsx +35 -0
- package/src/features/PluginStore/PluginItem/EditCustomPlugin.tsx +7 -2
- package/src/locales/default/chat.ts +10 -1
- package/src/locales/default/common.ts +1 -0
- package/src/services/config.ts +22 -0
- package/src/services/session.ts +6 -0
- package/src/services/topic.ts +4 -0
- package/src/store/chat/slices/message/selectors.ts +10 -0
- package/src/store/chat/slices/topic/action.ts +22 -0
- package/src/store/global/slices/common/action.ts +2 -0
- package/src/store/session/slices/session/action.test.ts +136 -0
- package/src/store/session/slices/session/action.ts +26 -6
- package/src/types/topic.ts +1 -0
- package/src/features/Conversation/components/ChatList/ActionsBar.tsx +0 -27
- package/src/features/Conversation/components/ChatList/Item.tsx +0 -236
- package/src/features/Conversation/container/index.tsx +0 -83
|
@@ -168,6 +168,41 @@ class _MessageModel extends BaseModel {
|
|
|
168
168
|
return this.table.where('sessionId').equals(sessionId).toArray();
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
queryByTopicId = async (topicId: string) => {
|
|
172
|
+
const dbMessages = await this.table.where('topicId').equals(topicId).toArray();
|
|
173
|
+
|
|
174
|
+
return dbMessages.map((message) => this.mapToChatMessage(message));
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
async duplicateMessages(messages: ChatMessage[]): Promise<ChatMessage[]> {
|
|
178
|
+
const duplicatedMessages = await this.createDuplicateMessages(messages);
|
|
179
|
+
// 批量添加复制后的消息到数据库
|
|
180
|
+
await this.batchCreate(duplicatedMessages);
|
|
181
|
+
return duplicatedMessages;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async createDuplicateMessages(messages: ChatMessage[]): Promise<ChatMessage[]> {
|
|
185
|
+
// 创建一个映射来存储原始消息ID和复制消息ID之间的关系
|
|
186
|
+
const idMapping = new Map<string, string>();
|
|
187
|
+
|
|
188
|
+
// 首先复制所有消息,并为每个复制的消息生成新的ID
|
|
189
|
+
const duplicatedMessages = messages.map((originalMessage) => {
|
|
190
|
+
const newId = nanoid();
|
|
191
|
+
idMapping.set(originalMessage.id, newId);
|
|
192
|
+
|
|
193
|
+
return { ...originalMessage, id: newId };
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// 更新 parentId 为复制后的新ID
|
|
197
|
+
for (const duplicatedMessage of duplicatedMessages) {
|
|
198
|
+
if (duplicatedMessage.parentId && idMapping.has(duplicatedMessage.parentId)) {
|
|
199
|
+
duplicatedMessage.parentId = idMapping.get(duplicatedMessage.parentId);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return duplicatedMessages;
|
|
204
|
+
}
|
|
205
|
+
|
|
171
206
|
private mapChatMessageToDBMessage(message: ChatMessage): DB_Message {
|
|
172
207
|
const { extra, ...messageData } = message;
|
|
173
208
|
|
|
@@ -2,6 +2,7 @@ import { DeepPartial } from 'utility-types';
|
|
|
2
2
|
|
|
3
3
|
import { DEFAULT_AGENT_LOBE_SESSION } from '@/const/session';
|
|
4
4
|
import { BaseModel } from '@/database/core';
|
|
5
|
+
import { DBModel } from '@/database/core/types/db';
|
|
5
6
|
import { DB_Session, DB_SessionSchema } from '@/database/schemas/session';
|
|
6
7
|
import { LobeAgentConfig } from '@/types/agent';
|
|
7
8
|
import { LobeAgentSession, LobeSessions, SessionGroupKey } from '@/types/session';
|
|
@@ -79,7 +80,7 @@ class _SessionModel extends BaseModel {
|
|
|
79
80
|
return this.table.clear();
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
async findById(id: string) {
|
|
83
|
+
async findById(id: string): Promise<DBModel<DB_Session>> {
|
|
83
84
|
return this.table.get(id);
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -155,6 +156,15 @@ class _SessionModel extends BaseModel {
|
|
|
155
156
|
console.timeEnd('queryByKeyword');
|
|
156
157
|
return data;
|
|
157
158
|
}
|
|
159
|
+
|
|
160
|
+
async duplicate(id: string, newTitle?: string) {
|
|
161
|
+
const session = await this.findById(id);
|
|
162
|
+
if (!session) return;
|
|
163
|
+
|
|
164
|
+
const newSession = merge(session, { meta: { title: newTitle } });
|
|
165
|
+
|
|
166
|
+
return this._add(newSession, uuid());
|
|
167
|
+
}
|
|
158
168
|
}
|
|
159
169
|
|
|
160
170
|
export const SessionModel = new _SessionModel();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { BaseModel } from '@/database/core';
|
|
2
|
+
import { DBModel } from '@/database/core/types/db';
|
|
3
|
+
import { MessageModel } from '@/database/models/message';
|
|
2
4
|
import { DB_Topic, DB_TopicSchema } from '@/database/schemas/topic';
|
|
3
5
|
import { ChatTopic } from '@/types/topic';
|
|
4
6
|
import { nanoid } from '@/utils/uuid';
|
|
@@ -53,14 +55,14 @@ class _TopicModel extends BaseModel {
|
|
|
53
55
|
// handle pageSize
|
|
54
56
|
const pagedTopics = sortedTopics.slice(offset, offset + pageSize);
|
|
55
57
|
|
|
56
|
-
return pagedTopics.map((i) => (
|
|
58
|
+
return pagedTopics.map((i) => this.mapToChatTopic(i));
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
async findBySessionId(sessionId: string) {
|
|
60
62
|
return this.table.where({ sessionId }).toArray();
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
async findById(id: string) {
|
|
65
|
+
async findById(id: string): Promise<DBModel<DB_Topic>> {
|
|
64
66
|
return this.table.get(id);
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -198,6 +200,36 @@ class _TopicModel extends BaseModel {
|
|
|
198
200
|
console.timeEnd('queryTopicsByKeyword');
|
|
199
201
|
return uniqueTopics.map((i) => ({ ...i, favorite: !!i.favorite }));
|
|
200
202
|
}
|
|
203
|
+
|
|
204
|
+
async duplicateTopic(topicId: string, newTitle?: string) {
|
|
205
|
+
return this.db.transaction('rw', this.db.topics, this.db.messages, async () => {
|
|
206
|
+
// Step 1: get DB_Topic
|
|
207
|
+
const topic = await this.findById(topicId);
|
|
208
|
+
|
|
209
|
+
if (!topic) {
|
|
210
|
+
throw new Error(`Topic with id ${topicId} not found`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Step 3: 查询与 `topic` 关联的 `messages`
|
|
214
|
+
const originalMessages = await MessageModel.queryByTopicId(topicId);
|
|
215
|
+
|
|
216
|
+
const duplicateMessages = await MessageModel.duplicateMessages(originalMessages);
|
|
217
|
+
|
|
218
|
+
const { id } = await this.create({
|
|
219
|
+
...this.mapToChatTopic(topic),
|
|
220
|
+
messages: duplicateMessages.map((m) => m.id),
|
|
221
|
+
sessionId: topic.sessionId!,
|
|
222
|
+
title: newTitle || topic.title,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
return id;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private mapToChatTopic = (dbTopic: DBModel<DB_Topic>): ChatTopic => ({
|
|
230
|
+
...dbTopic,
|
|
231
|
+
favorite: !!dbTopic.favorite,
|
|
232
|
+
});
|
|
201
233
|
}
|
|
202
234
|
|
|
203
235
|
export const TopicModel = new _TopicModel();
|
|
@@ -2,7 +2,6 @@ import { ActionIcon } from '@lobehub/ui';
|
|
|
2
2
|
import { Popconfirm } from 'antd';
|
|
3
3
|
import { Eraser } from 'lucide-react';
|
|
4
4
|
import { memo, useCallback } from 'react';
|
|
5
|
-
import { useHotkeys } from 'react-hotkeys-hook';
|
|
6
5
|
import { useTranslation } from 'react-i18next';
|
|
7
6
|
|
|
8
7
|
import HotKeys from '@/components/HotKeys';
|
|
@@ -21,11 +20,6 @@ const Clear = memo(() => {
|
|
|
21
20
|
clearImageList();
|
|
22
21
|
}, []);
|
|
23
22
|
|
|
24
|
-
useHotkeys(hotkeys, resetConversation, {
|
|
25
|
-
enableOnFormTags: true,
|
|
26
|
-
preventDefault: true,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
23
|
return (
|
|
30
24
|
<Popconfirm
|
|
31
25
|
cancelText={t('cancel', { ns: 'common' })}
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { ActionIconGroup } from '@lobehub/ui';
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
|
-
import { RenderAction } from '../components/ChatList';
|
|
5
4
|
import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
|
|
5
|
+
import { RenderAction } from '../types';
|
|
6
6
|
import { ErrorActionsBar } from './Error';
|
|
7
7
|
import { useCustomActions } from './customAction';
|
|
8
8
|
|
|
9
|
-
export const AssistantActionsBar: RenderAction = memo(({
|
|
10
|
-
const { regenerate, edit, copy, divider, del } = useChatListActionsBar(
|
|
9
|
+
export const AssistantActionsBar: RenderAction = memo(({ id, onActionClick, error }) => {
|
|
10
|
+
const { regenerate, edit, delAndRegenerate, copy, divider, del } = useChatListActionsBar();
|
|
11
11
|
const { translate, tts } = useCustomActions();
|
|
12
|
+
|
|
12
13
|
if (id === 'default') return;
|
|
13
14
|
|
|
14
|
-
if (error) return <ErrorActionsBar onActionClick={onActionClick}
|
|
15
|
+
if (error) return <ErrorActionsBar onActionClick={onActionClick} />;
|
|
15
16
|
|
|
16
17
|
return (
|
|
17
18
|
<ActionIconGroup
|
|
18
|
-
dropdownMenu={[
|
|
19
|
+
dropdownMenu={[
|
|
20
|
+
edit,
|
|
21
|
+
copy,
|
|
22
|
+
regenerate,
|
|
23
|
+
divider,
|
|
24
|
+
tts,
|
|
25
|
+
translate,
|
|
26
|
+
divider,
|
|
27
|
+
delAndRegenerate,
|
|
28
|
+
del,
|
|
29
|
+
]}
|
|
19
30
|
items={[regenerate, copy]}
|
|
20
31
|
onActionClick={onActionClick}
|
|
21
32
|
type="ghost"
|
|
@@ -4,8 +4,8 @@ import { memo } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
|
|
6
6
|
|
|
7
|
-
export const ErrorActionsBar = memo<ActionsBarProps>(({
|
|
8
|
-
const { regenerate, del } = useChatListActionsBar(
|
|
7
|
+
export const ErrorActionsBar = memo<ActionsBarProps>(({ onActionClick }) => {
|
|
8
|
+
const { regenerate, del } = useChatListActionsBar();
|
|
9
9
|
|
|
10
10
|
return <ActionIconGroup items={[regenerate, del]} onActionClick={onActionClick} type="ghost" />;
|
|
11
11
|
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ActionIconGroup } from '@lobehub/ui';
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
|
-
import { RenderAction } from '../components/ChatList';
|
|
5
4
|
import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
|
|
5
|
+
import { RenderAction } from '../types';
|
|
6
|
+
|
|
7
|
+
export const DefaultActionsBar: RenderAction = memo(({ onActionClick }) => {
|
|
8
|
+
const { del } = useChatListActionsBar();
|
|
6
9
|
|
|
7
|
-
export const DefaultActionsBar: RenderAction = memo(({ text, onActionClick }) => {
|
|
8
|
-
const { del } = useChatListActionsBar(text);
|
|
9
10
|
return (
|
|
10
11
|
<ActionIconGroup dropdownMenu={[del]} items={[]} onActionClick={onActionClick} type="ghost" />
|
|
11
12
|
);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ActionIconGroup } from '@lobehub/ui';
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
|
-
import { RenderAction } from '../components/ChatList';
|
|
5
4
|
import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
|
|
5
|
+
import { RenderAction } from '../types';
|
|
6
6
|
|
|
7
|
-
export const FunctionActionsBar: RenderAction = memo(({
|
|
8
|
-
const { regenerate, divider, del } = useChatListActionsBar(
|
|
7
|
+
export const FunctionActionsBar: RenderAction = memo(({ onActionClick }) => {
|
|
8
|
+
const { regenerate, divider, del } = useChatListActionsBar();
|
|
9
9
|
return (
|
|
10
10
|
<ActionIconGroup
|
|
11
11
|
dropdownMenu={[regenerate, divider, del]}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ActionIconGroup } from '@lobehub/ui';
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
|
-
import { RenderAction } from '../components/ChatList';
|
|
5
4
|
import { useChatListActionsBar } from '../hooks/useChatListActionsBar';
|
|
5
|
+
import { RenderAction } from '../types';
|
|
6
6
|
import { useCustomActions } from './customAction';
|
|
7
7
|
|
|
8
|
-
export const UserActionsBar: RenderAction = memo(({
|
|
9
|
-
const { regenerate, edit, copy, divider, del } = useChatListActionsBar(
|
|
8
|
+
export const UserActionsBar: RenderAction = memo(({ onActionClick }) => {
|
|
9
|
+
const { regenerate, edit, copy, divider, del } = useChatListActionsBar();
|
|
10
10
|
const { translate, tts } = useCustomActions();
|
|
11
11
|
|
|
12
12
|
return (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ActionIconGroupItems } from '@lobehub/ui/es/ActionIconGroup';
|
|
2
2
|
import { LanguagesIcon, Play } from 'lucide-react';
|
|
3
|
+
import { useMemo } from 'react';
|
|
3
4
|
import { useTranslation } from 'react-i18next';
|
|
4
5
|
|
|
5
6
|
import { localeOptions } from '@/locales/resources';
|
|
@@ -23,8 +24,5 @@ export const useCustomActions = () => {
|
|
|
23
24
|
label: t('tts.action'),
|
|
24
25
|
} as ActionIconGroupItems;
|
|
25
26
|
|
|
26
|
-
return {
|
|
27
|
-
translate,
|
|
28
|
-
tts,
|
|
29
|
-
};
|
|
27
|
+
return useMemo(() => ({ translate, tts }), []);
|
|
30
28
|
};
|
|
@@ -1,67 +1,72 @@
|
|
|
1
|
+
import { copyToClipboard } from '@lobehub/ui';
|
|
2
|
+
import { App } from 'antd';
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
1
6
|
import { useChatStore } from '@/store/chat';
|
|
7
|
+
import { LLMRoleType } from '@/types/llm';
|
|
2
8
|
|
|
3
|
-
import {
|
|
9
|
+
import { OnActionsClick, RenderAction } from '../types';
|
|
4
10
|
import { AssistantActionsBar } from './Assistant';
|
|
5
11
|
import { DefaultActionsBar } from './Fallback';
|
|
6
12
|
import { FunctionActionsBar } from './Function';
|
|
7
13
|
import { UserActionsBar } from './User';
|
|
8
14
|
|
|
9
|
-
export const renderActions:
|
|
15
|
+
export const renderActions: Record<LLMRoleType, RenderAction> = {
|
|
10
16
|
assistant: AssistantActionsBar,
|
|
11
17
|
function: FunctionActionsBar,
|
|
12
18
|
system: DefaultActionsBar,
|
|
13
19
|
user: UserActionsBar,
|
|
14
20
|
};
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
trigger: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const useActionsClick = (): ChatListProps['onActionsClick'] => {
|
|
22
|
+
export const useActionsClick = (): OnActionsClick => {
|
|
23
|
+
const { t } = useTranslation('common');
|
|
22
24
|
const [deleteMessage, resendMessage, translateMessage, ttsMessage] = useChatStore((s) => [
|
|
23
25
|
s.deleteMessage,
|
|
24
26
|
s.resendMessage,
|
|
25
27
|
s.translateMessage,
|
|
26
28
|
s.ttsMessage,
|
|
27
29
|
]);
|
|
30
|
+
const { message } = App.useApp();
|
|
31
|
+
|
|
32
|
+
return useCallback<OnActionsClick>(async (action, { id, content, error }) => {
|
|
33
|
+
switch (action.key) {
|
|
34
|
+
case 'copy': {
|
|
35
|
+
await copyToClipboard(content);
|
|
36
|
+
message.success(t('copySuccess', { defaultValue: 'Copy Success' }));
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
case 'del': {
|
|
41
|
+
deleteMessage(id);
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
case 'regenerate': {
|
|
46
|
+
resendMessage(id);
|
|
47
|
+
// if this message is an error message, we need to delete it
|
|
48
|
+
if (error) deleteMessage(id);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
case 'delAndRegenerate': {
|
|
53
|
+
resendMessage(id);
|
|
54
|
+
deleteMessage(id);
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
28
57
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
trigger: action.key === 'del',
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
onClick: () => {
|
|
39
|
-
resendMessage(id);
|
|
40
|
-
// if this message is an error message, we need to delete it
|
|
41
|
-
if (error) deleteMessage(id);
|
|
42
|
-
},
|
|
43
|
-
trigger: action.key === 'regenerate',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
onClick: () => {
|
|
47
|
-
ttsMessage(id);
|
|
48
|
-
},
|
|
49
|
-
trigger: action.key === 'tts',
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
onClick: () => {
|
|
53
|
-
/**
|
|
54
|
-
* @description Click the menu item with translate item, the result is:
|
|
55
|
-
* @key 'en-US'
|
|
56
|
-
* @keyPath ['en-US','translate']
|
|
57
|
-
*/
|
|
58
|
-
const lang = action.keyPath[0];
|
|
59
|
-
translateMessage(id, lang);
|
|
60
|
-
},
|
|
61
|
-
trigger: action.keyPath.at(-1) === 'translate',
|
|
62
|
-
},
|
|
63
|
-
];
|
|
58
|
+
case 'tts': {
|
|
59
|
+
ttsMessage(id);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
if (action.keyPath.at(-1) === 'translate') {
|
|
65
|
+
// click the menu item with translate item, the result is:
|
|
66
|
+
// key: 'en-US'
|
|
67
|
+
// keyPath: ['en-US','translate']
|
|
68
|
+
const lang = action.keyPath[0];
|
|
69
|
+
translateMessage(id, lang);
|
|
70
|
+
}
|
|
71
|
+
}, []);
|
|
67
72
|
};
|
|
@@ -8,7 +8,7 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
8
8
|
import { useChatStore } from '@/store/chat';
|
|
9
9
|
import { useGlobalStore } from '@/store/global';
|
|
10
10
|
|
|
11
|
-
import { RenderErrorMessage } from '../
|
|
11
|
+
import { RenderErrorMessage } from '../types';
|
|
12
12
|
import APIKeyForm from './ApiKeyForm';
|
|
13
13
|
import { ErrorActionContainer, FormAction } from './style';
|
|
14
14
|
|
|
@@ -2,7 +2,7 @@ import { Highlighter } from '@lobehub/ui';
|
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
|
4
4
|
|
|
5
|
-
import { RenderErrorMessage } from '../
|
|
5
|
+
import { RenderErrorMessage } from '../types';
|
|
6
6
|
import OpenAPIKey from './OpenAPIKey';
|
|
7
7
|
|
|
8
8
|
interface OpenAIError {
|
|
@@ -2,7 +2,7 @@ import { Highlighter } from '@lobehub/ui';
|
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
|
4
4
|
|
|
5
|
-
import { RenderErrorMessage } from '../../
|
|
5
|
+
import { RenderErrorMessage } from '../../types';
|
|
6
6
|
|
|
7
7
|
interface OpenAIError {
|
|
8
8
|
code: 'invalid_api_key' | string;
|
|
@@ -11,7 +11,7 @@ import { useChatStore } from '@/store/chat';
|
|
|
11
11
|
import { pluginHelpers, useToolStore } from '@/store/tool';
|
|
12
12
|
import { pluginSelectors } from '@/store/tool/selectors';
|
|
13
13
|
|
|
14
|
-
import { RenderErrorMessage } from '../../
|
|
14
|
+
import { RenderErrorMessage } from '../../types';
|
|
15
15
|
import { ErrorActionContainer, useStyles } from '../style';
|
|
16
16
|
|
|
17
17
|
const PluginSettings: RenderErrorMessage['Render'] = memo(({ id, plugin }) => {
|
|
@@ -2,14 +2,14 @@ import { PluginErrorType } from '@lobehub/chat-plugin-sdk';
|
|
|
2
2
|
|
|
3
3
|
import { ChatErrorType } from '@/types/fetch';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { RenderErrorMessage } from '../types';
|
|
6
6
|
import InvalidAccess from './InvalidAccess';
|
|
7
7
|
import OpenAPIKey from './OpenAPIKey';
|
|
8
8
|
import OpenAiBizError from './OpenAiBizError';
|
|
9
9
|
import PluginError from './Plugin/PluginError';
|
|
10
10
|
import PluginSettings from './Plugin/PluginSettings';
|
|
11
11
|
|
|
12
|
-
export const renderErrorMessages:
|
|
12
|
+
export const renderErrorMessages: Record<string, RenderErrorMessage> = {
|
|
13
13
|
[PluginErrorType.PluginMarketIndexNotFound]: {
|
|
14
14
|
Render: PluginError,
|
|
15
15
|
},
|
|
@@ -8,41 +8,44 @@ import { useSessionStore } from '@/store/session';
|
|
|
8
8
|
import { agentSelectors } from '@/store/session/selectors';
|
|
9
9
|
import { ChatMessage } from '@/types/message';
|
|
10
10
|
|
|
11
|
+
import { RenderMessageExtra } from '../types';
|
|
11
12
|
import ExtraContainer from './ExtraContainer';
|
|
12
13
|
import TTS from './TTS';
|
|
13
14
|
import Translate from './Translate';
|
|
14
15
|
|
|
15
|
-
export const AssistantMessageExtra = memo<ChatMessage>(
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
export const AssistantMessageExtra: RenderMessageExtra = memo<ChatMessage>(
|
|
17
|
+
({ extra, id, content }) => {
|
|
18
|
+
const model = useSessionStore(agentSelectors.currentAgentModel);
|
|
19
|
+
const loading = useChatStore((s) => s.chatLoadingId === id);
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const showModelTag = extra?.fromModel && model !== extra?.fromModel;
|
|
22
|
+
const showTranslate = !!extra?.translate;
|
|
23
|
+
const showTTS = !!extra?.tts;
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
const showExtra = showModelTag || showTranslate || showTTS;
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
if (!showExtra) return;
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
)}
|
|
34
|
-
<>
|
|
35
|
-
{extra?.tts && (
|
|
36
|
-
<ExtraContainer>
|
|
37
|
-
<TTS content={content} id={id} loading={loading} {...extra?.tts} />
|
|
38
|
-
</ExtraContainer>
|
|
29
|
+
return (
|
|
30
|
+
<Flexbox gap={8} style={{ marginTop: 8 }}>
|
|
31
|
+
{showModelTag && (
|
|
32
|
+
<div>
|
|
33
|
+
<Tag icon={<SiOpenai size={'1em'} />}>{extra?.fromModel as string}</Tag>
|
|
34
|
+
</div>
|
|
39
35
|
)}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
36
|
+
<>
|
|
37
|
+
{extra?.tts && (
|
|
38
|
+
<ExtraContainer>
|
|
39
|
+
<TTS content={content} id={id} loading={loading} {...extra?.tts} />
|
|
40
|
+
</ExtraContainer>
|
|
41
|
+
)}
|
|
42
|
+
{extra?.translate && (
|
|
43
|
+
<ExtraContainer>
|
|
44
|
+
<Translate id={id} loading={loading} {...extra?.translate} />
|
|
45
|
+
</ExtraContainer>
|
|
46
|
+
)}
|
|
47
|
+
</>
|
|
48
|
+
</Flexbox>
|
|
49
|
+
);
|
|
50
|
+
},
|
|
51
|
+
);
|
|
@@ -3,11 +3,12 @@ import { memo } from 'react';
|
|
|
3
3
|
import { useChatStore } from '@/store/chat';
|
|
4
4
|
import { ChatMessage } from '@/types/message';
|
|
5
5
|
|
|
6
|
+
import { RenderMessageExtra } from '../types';
|
|
6
7
|
import ExtraContainer from './ExtraContainer';
|
|
7
8
|
import TTS from './TTS';
|
|
8
9
|
import Translate from './Translate';
|
|
9
10
|
|
|
10
|
-
export const UserMessageExtra = memo<ChatMessage>(({ extra, id, content }) => {
|
|
11
|
+
export const UserMessageExtra: RenderMessageExtra = memo<ChatMessage>(({ extra, id, content }) => {
|
|
11
12
|
const loading = useChatStore((s) => s.chatLoadingId === id);
|
|
12
13
|
|
|
13
14
|
const showTranslate = !!extra?.translate;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RenderMessageExtra } from '../types';
|
|
2
2
|
import { AssistantMessageExtra } from './Assistant';
|
|
3
3
|
import { UserMessageExtra } from './User';
|
|
4
4
|
|
|
5
|
-
export const renderMessagesExtra:
|
|
6
|
-
assistant: AssistantMessageExtra
|
|
7
|
-
user: UserMessageExtra
|
|
5
|
+
export const renderMessagesExtra: Record<string, RenderMessageExtra> = {
|
|
6
|
+
assistant: AssistantMessageExtra,
|
|
7
|
+
user: UserMessageExtra,
|
|
8
8
|
};
|
|
@@ -6,20 +6,20 @@ import { useSessionStore } from '@/store/session';
|
|
|
6
6
|
import { sessionSelectors } from '@/store/session/selectors';
|
|
7
7
|
import { pathString } from '@/utils/url';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { OnAvatarsClick, RenderMessage } from '../types';
|
|
10
10
|
import { AssistantMessage } from './Assistant';
|
|
11
11
|
import { DefaultMessage } from './Default';
|
|
12
12
|
import { FunctionMessage } from './Function';
|
|
13
13
|
import { UserMessage } from './User';
|
|
14
14
|
|
|
15
|
-
export const renderMessages:
|
|
15
|
+
export const renderMessages: Record<string, RenderMessage> = {
|
|
16
16
|
assistant: AssistantMessage,
|
|
17
17
|
default: DefaultMessage,
|
|
18
18
|
function: FunctionMessage,
|
|
19
19
|
user: UserMessage,
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
export const useAvatarsClick = ():
|
|
22
|
+
export const useAvatarsClick = (): OnAvatarsClick => {
|
|
23
23
|
const [isInbox] = useSessionStore((s) => [sessionSelectors.isInboxSession(s)]);
|
|
24
24
|
const [toggleSystemRole] = useGlobalStore((s) => [s.toggleSystemRole]);
|
|
25
25
|
const { mobile } = useResponsive();
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ActionEvent, ActionIconGroup, type ActionIconGroupProps } from '@lobehub/ui';
|
|
2
|
+
import isEqual from 'fast-deep-equal';
|
|
3
|
+
import { memo, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
import { useChatStore } from '@/store/chat';
|
|
6
|
+
import { chatSelectors } from '@/store/chat/selectors';
|
|
7
|
+
import { useSessionStore } from '@/store/session';
|
|
8
|
+
import { agentSelectors } from '@/store/session/selectors';
|
|
9
|
+
|
|
10
|
+
import { renderActions, useActionsClick } from '../../Actions';
|
|
11
|
+
import { useChatListActionsBar } from '../../hooks/useChatListActionsBar';
|
|
12
|
+
|
|
13
|
+
export type ActionsBarProps = ActionIconGroupProps;
|
|
14
|
+
|
|
15
|
+
const ActionsBar = memo<ActionsBarProps>((props) => {
|
|
16
|
+
const { regenerate, edit, copy, divider, del } = useChatListActionsBar();
|
|
17
|
+
return (
|
|
18
|
+
<ActionIconGroup
|
|
19
|
+
dropdownMenu={[edit, copy, regenerate, divider, del]}
|
|
20
|
+
items={[regenerate, edit]}
|
|
21
|
+
type="ghost"
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
interface ActionsProps {
|
|
28
|
+
index: number;
|
|
29
|
+
setEditing: (edit: boolean) => void;
|
|
30
|
+
}
|
|
31
|
+
const Actions = memo<ActionsProps>(({ index, setEditing }) => {
|
|
32
|
+
const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
|
33
|
+
|
|
34
|
+
const item = useChatStore(
|
|
35
|
+
(s) => chatSelectors.currentChatsWithGuideMessage(meta)(s)[index],
|
|
36
|
+
isEqual,
|
|
37
|
+
);
|
|
38
|
+
const onActionsClick = useActionsClick();
|
|
39
|
+
|
|
40
|
+
const handleActionClick = useCallback(
|
|
41
|
+
async (action: ActionEvent) => {
|
|
42
|
+
switch (action.key) {
|
|
43
|
+
case 'edit': {
|
|
44
|
+
setEditing(true);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onActionsClick(action, item);
|
|
49
|
+
},
|
|
50
|
+
[item],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const RenderFunction = renderActions[item?.role] ?? ActionsBar;
|
|
54
|
+
|
|
55
|
+
return <RenderFunction {...item} onActionClick={handleActionClick} />;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export default Actions;
|