@lobehub/chat 1.123.3 → 1.124.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 +50 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/chat.json +6 -2
- package/locales/ar/editor.json +47 -0
- package/locales/bg-BG/chat.json +6 -2
- package/locales/bg-BG/editor.json +47 -0
- package/locales/de-DE/chat.json +6 -2
- package/locales/de-DE/editor.json +47 -0
- package/locales/en-US/chat.json +6 -2
- package/locales/en-US/editor.json +47 -0
- package/locales/es-ES/chat.json +6 -2
- package/locales/es-ES/editor.json +47 -0
- package/locales/es-ES/models.json +3 -1
- package/locales/fa-IR/chat.json +6 -2
- package/locales/fa-IR/editor.json +47 -0
- package/locales/fr-FR/chat.json +6 -2
- package/locales/fr-FR/editor.json +47 -0
- package/locales/it-IT/chat.json +6 -2
- package/locales/it-IT/editor.json +47 -0
- package/locales/ja-JP/chat.json +6 -2
- package/locales/ja-JP/editor.json +47 -0
- package/locales/ko-KR/chat.json +6 -2
- package/locales/ko-KR/editor.json +47 -0
- package/locales/ko-KR/models.json +3 -1
- package/locales/nl-NL/chat.json +6 -2
- package/locales/nl-NL/editor.json +47 -0
- package/locales/nl-NL/models.json +3 -1
- package/locales/pl-PL/chat.json +6 -2
- package/locales/pl-PL/editor.json +47 -0
- package/locales/pt-BR/chat.json +6 -2
- package/locales/pt-BR/editor.json +47 -0
- package/locales/ru-RU/chat.json +6 -2
- package/locales/ru-RU/editor.json +47 -0
- package/locales/tr-TR/chat.json +6 -2
- package/locales/tr-TR/editor.json +47 -0
- package/locales/vi-VN/chat.json +6 -2
- package/locales/vi-VN/editor.json +47 -0
- package/locales/zh-CN/chat.json +6 -2
- package/locales/zh-CN/editor.json +47 -0
- package/locales/zh-TW/chat.json +6 -2
- package/locales/zh-TW/editor.json +47 -0
- package/locales/zh-TW/models.json +3 -1
- package/next.config.ts +4 -0
- package/package.json +4 -2
- package/packages/const/src/layoutTokens.ts +1 -0
- package/packages/types/src/index.ts +1 -0
- package/packages/utils/src/index.ts +1 -0
- package/src/app/(backend)/webapi/chat/[provider]/route.ts +1 -1
- package/src/app/(backend)/webapi/chat/vertexai/route.ts +1 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/{Footer/MessageFromUrl.tsx → MessageFromUrl.tsx} +3 -2
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +129 -28
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +44 -66
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +141 -0
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx +7 -1
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/InboxWelcome/QuestionSuggest.tsx +3 -2
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/OpeningQuestions.tsx +3 -2
- package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +18 -2
- package/src/features/ChatInput/ActionBar/STT/common.tsx +41 -47
- package/src/features/ChatInput/{Topic → ActionBar/SaveTopic}/index.tsx +15 -4
- package/src/features/ChatInput/ActionBar/Typo/index.tsx +22 -0
- package/src/features/ChatInput/ActionBar/components/Action.tsx +4 -0
- package/src/features/ChatInput/ActionBar/config.ts +7 -1
- package/src/features/ChatInput/ActionBar/index.tsx +40 -51
- package/src/features/ChatInput/ChatInputProvider.tsx +54 -0
- package/src/features/ChatInput/Desktop/FilePreview/FileItem/index.tsx +20 -11
- package/src/features/ChatInput/Desktop/FilePreview/FileList.tsx +16 -15
- package/src/features/ChatInput/Desktop/index.tsx +81 -68
- package/src/features/ChatInput/InputEditor/index.tsx +134 -0
- package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/File.tsx +1 -2
- package/src/features/ChatInput/Mobile/FilePreview/index.tsx +44 -0
- package/src/features/ChatInput/Mobile/index.tsx +72 -0
- package/src/features/ChatInput/SendArea/ExpandButton.tsx +30 -0
- package/src/features/ChatInput/SendArea/SendButton.tsx +29 -0
- package/src/features/ChatInput/SendArea/ShortcutHint.tsx +52 -0
- package/src/features/ChatInput/SendArea/index.tsx +36 -0
- package/src/features/ChatInput/StoreUpdater.tsx +41 -0
- package/src/features/ChatInput/TypoBar/index.tsx +139 -0
- package/src/features/ChatInput/hooks/useChatInputEditor.ts +36 -0
- package/src/features/ChatInput/index.ts +7 -0
- package/src/features/ChatInput/store/action.ts +75 -0
- package/src/features/ChatInput/store/index.ts +23 -0
- package/src/features/ChatInput/store/initialState.ts +54 -0
- package/src/features/ChatInput/store/selectors.ts +5 -0
- package/src/features/Conversation/components/BackBottom/style.ts +1 -1
- package/src/features/Conversation/components/SkeletonList.tsx +10 -3
- package/src/features/Conversation/components/VirtualizedList/index.tsx +53 -44
- package/src/features/Conversation/components/WideScreenContainer/index.tsx +43 -0
- package/src/features/Portal/Thread/Chat/ChatInput/index.tsx +49 -42
- package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +48 -22
- package/src/features/Portal/Thread/Chat/index.tsx +2 -2
- package/src/features/Portal/Thread/Header/index.tsx +1 -1
- package/src/hooks/useHotkeys/chatScope.ts +5 -3
- package/src/layout/GlobalProvider/Editor.tsx +27 -0
- package/src/layout/GlobalProvider/Locale.tsx +3 -23
- package/src/libs/trpc/client/lambda.ts +76 -63
- package/src/locales/default/chat.ts +7 -2
- package/src/locales/default/editor.ts +47 -0
- package/src/locales/default/index.ts +2 -0
- package/src/services/aiChat.ts +8 -2
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +42 -33
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +174 -35
- package/src/store/chat/slices/aiChat/initialState.ts +19 -0
- package/src/store/chat/slices/aiChat/selectors.ts +18 -0
- package/src/store/global/action.test.ts +6 -5
- package/src/store/global/actions/__tests__/general.test.ts +6 -6
- package/src/store/global/actions/workspacePane.ts +6 -0
- package/src/store/global/initialState.ts +2 -4
- package/src/store/global/selectors/systemStatus.test.ts +1 -2
- package/src/store/global/selectors/systemStatus.ts +2 -5
- package/src/app/(backend)/webapi/chat/anthropic/route.test.ts +0 -30
- package/src/app/(backend)/webapi/chat/anthropic/route.ts +0 -21
- package/src/app/(backend)/webapi/chat/google/route.test.ts +0 -35
- package/src/app/(backend)/webapi/chat/google/route.ts +0 -25
- package/src/app/(backend)/webapi/chat/groq/route.test.ts +0 -29
- package/src/app/(backend)/webapi/chat/groq/route.ts +0 -21
- package/src/app/(backend)/webapi/chat/openai/route.test.ts +0 -30
- package/src/app/(backend)/webapi/chat/openai/route.ts +0 -26
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +0 -104
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/ShortcutHint.tsx +0 -40
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +0 -125
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx +0 -332
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx +0 -29
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/index.tsx +0 -33
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/Container.tsx +0 -41
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/index.tsx +0 -156
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Send.tsx +0 -33
- package/src/features/ChatInput/Desktop/Header/index.tsx +0 -30
- package/src/features/ChatInput/Desktop/InputArea/index.tsx +0 -143
- package/src/features/ChatInput/Desktop/__tests__/useAutoFocus.test.ts +0 -45
- package/src/features/ChatInput/Desktop/useAutoFocus.ts +0 -13
- package/src/features/ChatInput/useSend.ts +0 -102
- package/src/features/Portal/Thread/Chat/ChatInput/Footer.tsx +0 -90
- package/src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx +0 -30
- package/src/libs/trpc/client/types.ts +0 -18
- /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/Image.tsx +0 -0
- /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/index.tsx +0 -0
- /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/style.ts +0 -0
@@ -4,63 +4,70 @@ import { Alert } from '@lobehub/ui';
|
|
4
4
|
import Link from 'next/link';
|
5
5
|
import { memo } from 'react';
|
6
6
|
import { Trans } from 'react-i18next';
|
7
|
+
import { Flexbox } from 'react-layout-kit';
|
7
8
|
|
8
|
-
import { ActionKeys } from '@/features/ChatInput
|
9
|
-
import
|
9
|
+
import { type ActionKeys, ChatInputProvider, DesktopChatInput } from '@/features/ChatInput';
|
10
|
+
import WideScreenContainer from '@/features/Conversation/components/WideScreenContainer';
|
11
|
+
import { useChatStore } from '@/store/chat';
|
10
12
|
import { useGlobalStore } from '@/store/global';
|
11
13
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
12
14
|
|
13
|
-
import
|
14
|
-
import TextArea from './TextArea';
|
15
|
+
import { useSendThreadMessage } from './useSend';
|
15
16
|
|
16
|
-
const
|
17
|
-
|
18
|
-
const rightActions = [] as ActionKeys[];
|
19
|
-
|
20
|
-
const renderTextArea = (onSend: () => void) => <TextArea onSend={onSend} />;
|
21
|
-
const renderFooter: FooterRender = (props) => <Footer {...props} />;
|
17
|
+
const threadActions: ActionKeys[] = ['typo', 'stt', 'portalToken'];
|
22
18
|
|
23
19
|
const Desktop = memo(() => {
|
24
|
-
const [
|
25
|
-
systemStatusSelectors.threadInputHeight(s),
|
20
|
+
const [hideThreadLimitAlert, updateSystemStatus] = useGlobalStore((s) => [
|
26
21
|
systemStatusSelectors.systemStatus(s).hideThreadLimitAlert,
|
27
22
|
s.updateSystemStatus,
|
28
23
|
]);
|
29
24
|
|
25
|
+
const { send, disabled, generating, stop } = useSendThreadMessage();
|
26
|
+
|
30
27
|
return (
|
31
|
-
|
28
|
+
<WideScreenContainer>
|
32
29
|
{!hideThreadLimitAlert && (
|
33
|
-
<
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
30
|
+
<Flexbox paddingBlock={'0 6px'} paddingInline={12}>
|
31
|
+
<Alert
|
32
|
+
closable
|
33
|
+
message={
|
34
|
+
<Trans i18nKey={'notSupportMultiModals'} ns={'thread'}>
|
35
|
+
子话题暂不支持文件/图片上传,如有需求,欢迎留言:
|
36
|
+
<Link
|
37
|
+
href={'https://github.com/lobehub/lobe-chat/discussions/4717'}
|
38
|
+
style={{ textDecoration: 'underline' }}
|
39
|
+
>
|
40
|
+
💬 讨论
|
41
|
+
</Link>
|
42
|
+
</Trans>
|
43
|
+
}
|
44
|
+
onClose={() => {
|
45
|
+
updateSystemStatus({ hideThreadLimitAlert: true });
|
46
|
+
}}
|
47
|
+
type={'info'}
|
48
|
+
/>
|
49
|
+
</Flexbox>
|
52
50
|
)}
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
|
52
|
+
<ChatInputProvider
|
53
|
+
chatInputEditorRef={(instance) => {
|
54
|
+
if (!instance) return;
|
55
|
+
useChatStore.setState({ threadInputEditor: instance });
|
56
|
+
}}
|
57
|
+
leftActions={threadActions}
|
58
|
+
onSend={() => {
|
59
|
+
send();
|
60
|
+
}}
|
61
|
+
sendButtonProps={{
|
62
|
+
disabled,
|
63
|
+
generating,
|
64
|
+
onStop: stop,
|
65
|
+
shape: 'round',
|
58
66
|
}}
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
</>
|
67
|
+
>
|
68
|
+
<DesktopChatInput />
|
69
|
+
</ChatInputProvider>
|
70
|
+
</WideScreenContainer>
|
64
71
|
);
|
65
72
|
});
|
66
73
|
|
@@ -1,5 +1,8 @@
|
|
1
|
-
import {
|
1
|
+
import { useMemo, useState } from 'react';
|
2
2
|
|
3
|
+
import { useGeminiChineseWarning } from '@/hooks/useGeminiChineseWarning';
|
4
|
+
import { getAgentStoreState } from '@/store/agent';
|
5
|
+
import { agentSelectors } from '@/store/agent/slices/chat';
|
3
6
|
import { useChatStore } from '@/store/chat';
|
4
7
|
import { threadSelectors } from '@/store/chat/selectors';
|
5
8
|
import { SendMessageParams } from '@/types/message';
|
@@ -10,41 +13,64 @@ export type UseSendMessageParams = Pick<
|
|
10
13
|
>;
|
11
14
|
|
12
15
|
export const useSendThreadMessage = () => {
|
16
|
+
const [loading, setLoading] = useState(false);
|
17
|
+
const canNotSend = useChatStore(threadSelectors.isSendButtonDisabledByMessage);
|
18
|
+
const generating = useChatStore((s) => threadSelectors.isThreadAIGenerating(s));
|
19
|
+
const stop = useChatStore((s) => s.stopGenerateMessage);
|
13
20
|
const [sendMessage, updateInputMessage] = useChatStore((s) => [
|
14
21
|
s.sendThreadMessage,
|
15
22
|
s.updateThreadInputMessage,
|
16
23
|
]);
|
24
|
+
const checkGeminiChineseWarning = useGeminiChineseWarning();
|
17
25
|
|
18
|
-
const
|
19
|
-
|
20
|
-
const canSend = !isSendButtonDisabledByMessage;
|
21
|
-
|
22
|
-
const send = useCallback((params: UseSendMessageParams = {}) => {
|
26
|
+
const handleSend = async (params: UseSendMessageParams = {}) => {
|
23
27
|
const store = useChatStore.getState();
|
28
|
+
|
24
29
|
if (threadSelectors.isThreadAIGenerating(store)) return;
|
30
|
+
const canNotSend = threadSelectors.isSendButtonDisabledByMessage(store);
|
31
|
+
|
32
|
+
if (canNotSend) return;
|
33
|
+
|
34
|
+
const threadInputEditor = store.threadInputEditor;
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
36
|
+
if (!threadInputEditor) {
|
37
|
+
console.warn('not found threadInputEditor instance');
|
38
|
+
return;
|
39
|
+
}
|
29
40
|
|
30
|
-
const
|
31
|
-
if (!canSend) return;
|
41
|
+
const inputMessage = threadInputEditor.getMarkdownContent();
|
32
42
|
|
33
43
|
// if there is no message and no image, then we should not send the message
|
34
|
-
if (!
|
44
|
+
if (!inputMessage) return;
|
35
45
|
|
36
|
-
|
46
|
+
// Check for Chinese text warning with Gemini model
|
47
|
+
const agentStore = getAgentStoreState();
|
48
|
+
const currentModel = agentSelectors.currentAgentModel(agentStore);
|
49
|
+
const shouldContinue = await checkGeminiChineseWarning({
|
50
|
+
model: currentModel,
|
51
|
+
prompt: inputMessage,
|
52
|
+
scenario: 'chat',
|
53
|
+
});
|
37
54
|
|
38
|
-
|
55
|
+
if (!shouldContinue) return;
|
56
|
+
|
57
|
+
updateInputMessage(inputMessage);
|
39
58
|
|
40
|
-
|
41
|
-
|
59
|
+
sendMessage({ message: inputMessage, ...params });
|
60
|
+
|
61
|
+
updateInputMessage('');
|
62
|
+
threadInputEditor.clearContent();
|
63
|
+
threadInputEditor.focus();
|
64
|
+
};
|
42
65
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
}
|
66
|
+
const send = async (params: UseSendMessageParams = {}) => {
|
67
|
+
setLoading(true);
|
68
|
+
await handleSend(params);
|
69
|
+
setLoading(false);
|
70
|
+
};
|
48
71
|
|
49
|
-
return useMemo(
|
72
|
+
return useMemo(
|
73
|
+
() => ({ disabled: canNotSend, generating, loading, send, stop }),
|
74
|
+
[canNotSend, send, generating, stop, loading],
|
75
|
+
);
|
50
76
|
};
|
@@ -11,7 +11,7 @@ interface ConversationProps {
|
|
11
11
|
}
|
12
12
|
|
13
13
|
const Conversation = memo<ConversationProps>(({ mobile }) => (
|
14
|
-
|
14
|
+
<>
|
15
15
|
<Suspense
|
16
16
|
fallback={
|
17
17
|
<Flexbox flex={1} height={'100%'}>
|
@@ -22,7 +22,7 @@ const Conversation = memo<ConversationProps>(({ mobile }) => (
|
|
22
22
|
<ChatList mobile={mobile} />
|
23
23
|
</Suspense>
|
24
24
|
<ChatInput />
|
25
|
-
|
25
|
+
</>
|
26
26
|
));
|
27
27
|
|
28
28
|
export default Conversation;
|
@@ -40,7 +40,7 @@ const Header = memo(() => {
|
|
40
40
|
paddingBlock={6}
|
41
41
|
paddingInline={8}
|
42
42
|
style={{
|
43
|
-
|
43
|
+
borderBottom: `1px solid ${theme.colorBorderSecondary}`,
|
44
44
|
}}
|
45
45
|
title={<Title />}
|
46
46
|
/>
|
@@ -2,8 +2,8 @@ import isEqual from 'fast-deep-equal';
|
|
2
2
|
import { useEffect } from 'react';
|
3
3
|
import { useHotkeysContext } from 'react-hotkeys-hook';
|
4
4
|
|
5
|
+
import { useSend } from '@/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend';
|
5
6
|
import { useClearCurrentMessages } from '@/features/ChatInput/ActionBar/Clear';
|
6
|
-
import { useSendMessage } from '@/features/ChatInput/useSend';
|
7
7
|
import { useOpenChatSettings } from '@/hooks/useInterceptingRoutes';
|
8
8
|
import { useActionSWR } from '@/libs/swr';
|
9
9
|
import { useChatStore } from '@/store/chat';
|
@@ -75,8 +75,10 @@ export const useToggleRightPanelHotkey = () => {
|
|
75
75
|
};
|
76
76
|
|
77
77
|
export const useAddUserMessageHotkey = () => {
|
78
|
-
const { send } =
|
79
|
-
return useHotkeyById(HotkeyEnum.AddUserMessage, () =>
|
78
|
+
const { send } = useSend();
|
79
|
+
return useHotkeyById(HotkeyEnum.AddUserMessage, () => {
|
80
|
+
send({ onlyAddUserMessage: true });
|
81
|
+
});
|
80
82
|
};
|
81
83
|
|
82
84
|
export const useClearCurrentMessagesHotkey = () => {
|
@@ -0,0 +1,27 @@
|
|
1
|
+
'use client';
|
2
|
+
|
3
|
+
import { EditorProvider } from '@lobehub/editor/react';
|
4
|
+
import { PropsWithChildren, memo, useMemo } from 'react';
|
5
|
+
import { useTranslation } from 'react-i18next';
|
6
|
+
|
7
|
+
const Editor = memo<PropsWithChildren>(({ children }) => {
|
8
|
+
const {
|
9
|
+
i18n: { language, getResourceBundle },
|
10
|
+
} = useTranslation('editor');
|
11
|
+
|
12
|
+
const localization = useMemo(() => getResourceBundle(language, 'editor'), [language]);
|
13
|
+
|
14
|
+
return (
|
15
|
+
<EditorProvider
|
16
|
+
config={{
|
17
|
+
locale: localization,
|
18
|
+
}}
|
19
|
+
>
|
20
|
+
{children}
|
21
|
+
</EditorProvider>
|
22
|
+
);
|
23
|
+
});
|
24
|
+
|
25
|
+
Editor.displayName = 'Editor';
|
26
|
+
|
27
|
+
export default Editor;
|
@@ -1,7 +1,6 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
3
|
import { ConfigProvider } from 'antd';
|
4
|
-
import { useTheme } from 'antd-style';
|
5
4
|
import dayjs from 'dayjs';
|
6
5
|
import { PropsWithChildren, memo, useEffect, useState } from 'react';
|
7
6
|
import { isRtlLang } from 'rtl-detect';
|
@@ -10,6 +9,8 @@ import { createI18nNext } from '@/locales/create';
|
|
10
9
|
import { isOnServerSide } from '@/utils/env';
|
11
10
|
import { getAntdLocale } from '@/utils/locale';
|
12
11
|
|
12
|
+
import Editor from './Editor';
|
13
|
+
|
13
14
|
const updateDayjs = async (lang: string) => {
|
14
15
|
// load default lang
|
15
16
|
let dayJSLocale;
|
@@ -36,7 +37,6 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) =
|
|
36
37
|
const [i18n] = useState(createI18nNext(defaultLang));
|
37
38
|
const [lang, setLang] = useState(defaultLang);
|
38
39
|
const [locale, setLocale] = useState(antdLocale);
|
39
|
-
const theme = useTheme();
|
40
40
|
|
41
41
|
// if run on server side, init i18n instance everytime
|
42
42
|
if (isOnServerSide) {
|
@@ -91,30 +91,10 @@ const Locale = memo<LocaleLayoutProps>(({ children, defaultLang, antdLocale }) =
|
|
91
91
|
Button: {
|
92
92
|
contentFontSizeSM: 12,
|
93
93
|
},
|
94
|
-
DatePicker: {
|
95
|
-
activeBorderColor: theme.colorBorder,
|
96
|
-
hoverBorderColor: theme.colorBorder,
|
97
|
-
},
|
98
|
-
Input: {
|
99
|
-
activeBorderColor: theme.colorBorder,
|
100
|
-
hoverBorderColor: theme.colorBorder,
|
101
|
-
},
|
102
|
-
InputNumber: {
|
103
|
-
activeBorderColor: theme.colorBorder,
|
104
|
-
hoverBorderColor: theme.colorBorder,
|
105
|
-
},
|
106
|
-
Mentions: {
|
107
|
-
activeBorderColor: theme.colorBorder,
|
108
|
-
hoverBorderColor: theme.colorBorder,
|
109
|
-
},
|
110
|
-
Select: {
|
111
|
-
activeBorderColor: theme.colorBorder,
|
112
|
-
hoverBorderColor: theme.colorBorder,
|
113
|
-
},
|
114
94
|
},
|
115
95
|
}}
|
116
96
|
>
|
117
|
-
{children}
|
97
|
+
<Editor>{children}</Editor>
|
118
98
|
</ConfigProvider>
|
119
99
|
);
|
120
100
|
});
|
@@ -1,81 +1,94 @@
|
|
1
1
|
import { ModelProvider } from '@lobechat/model-runtime';
|
2
|
-
import { createTRPCClient, httpBatchLink } from '@trpc/client';
|
2
|
+
import { TRPCLink, createTRPCClient, httpBatchLink } from '@trpc/client';
|
3
3
|
import { createTRPCReact } from '@trpc/react-query';
|
4
|
+
import { observable } from '@trpc/server/observable';
|
4
5
|
import debug from 'debug';
|
5
6
|
import superjson from 'superjson';
|
6
7
|
|
7
8
|
import { isDesktop } from '@/const/version';
|
8
9
|
import type { LambdaRouter } from '@/server/routers/lambda';
|
9
10
|
|
10
|
-
import { ErrorResponse } from './types';
|
11
|
-
|
12
11
|
const log = debug('lobe-image:lambda-client');
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
// handle error
|
14
|
+
const errorHandlingLink: TRPCLink<LambdaRouter> = () => {
|
15
|
+
return ({ op, next }) =>
|
16
|
+
observable((observer) =>
|
17
|
+
next(op).subscribe({
|
18
|
+
complete: () => observer.complete(),
|
19
|
+
error: async (err) => {
|
20
|
+
const showError = (op.context?.showNotification as boolean) ?? true;
|
21
|
+
|
22
|
+
if (showError) {
|
23
|
+
const status = err.data?.httpStatus as number;
|
24
|
+
|
25
|
+
const { loginRequired } = await import('@/components/Error/loginRequiredNotification');
|
26
|
+
const { fetchErrorNotification } = await import(
|
27
|
+
'@/components/Error/fetchErrorNotification'
|
28
|
+
);
|
29
|
+
|
30
|
+
switch (status) {
|
31
|
+
case 401: {
|
32
|
+
loginRequired.redirect();
|
33
|
+
break;
|
34
|
+
}
|
35
|
+
|
36
|
+
default: {
|
37
|
+
fetchErrorNotification.error({ errorMessage: err.message, status });
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
19
41
|
|
20
|
-
|
21
|
-
|
42
|
+
observer.error(err);
|
43
|
+
},
|
44
|
+
next: (value) => observer.next(value),
|
45
|
+
}),
|
46
|
+
);
|
47
|
+
};
|
22
48
|
|
23
|
-
|
24
|
-
|
49
|
+
// 2. httpBatchLink
|
50
|
+
const customHttpBatchLink = httpBatchLink({
|
51
|
+
fetch: async (input, init) => {
|
52
|
+
if (isDesktop) {
|
53
|
+
const { desktopRemoteRPCFetch } = await import('@/utils/electron/desktopRemoteRPCFetch');
|
25
54
|
|
26
55
|
// eslint-disable-next-line no-undef
|
27
|
-
const
|
28
|
-
|
29
|
-
if (
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
56
|
+
const res = await desktopRemoteRPCFetch(input as string, init as RequestInit);
|
57
|
+
|
58
|
+
if (res) return res;
|
59
|
+
}
|
60
|
+
|
61
|
+
// eslint-disable-next-line no-undef
|
62
|
+
return await fetch(input, init as RequestInit);
|
63
|
+
},
|
64
|
+
headers: async () => {
|
65
|
+
// dynamic import to avoid circular dependency
|
66
|
+
const { createHeaderWithAuth } = await import('@/services/_auth');
|
67
|
+
|
68
|
+
let provider: ModelProvider = ModelProvider.OpenAI;
|
69
|
+
// for image page, we need to get the provider from the store
|
70
|
+
log('Getting provider from store for image page: %s', location.pathname);
|
71
|
+
if (location.pathname === '/image') {
|
72
|
+
const { getImageStoreState } = await import('@/store/image');
|
73
|
+
const { imageGenerationConfigSelectors } = await import(
|
74
|
+
'@/store/image/slices/generationConfig/selectors'
|
75
|
+
);
|
76
|
+
provider = imageGenerationConfigSelectors.provider(getImageStoreState()) as ModelProvider;
|
77
|
+
log('Getting provider from store for image page: %s', provider);
|
78
|
+
}
|
79
|
+
|
80
|
+
// TODO: we need to support provider select for chat page
|
81
|
+
const headers = await createHeaderWithAuth({ provider });
|
82
|
+
log('Headers: %O', headers);
|
83
|
+
return headers;
|
84
|
+
},
|
85
|
+
maxURLLength: 2083,
|
86
|
+
transformer: superjson,
|
87
|
+
url: '/trpc/lambda',
|
88
|
+
});
|
39
89
|
|
40
|
-
|
41
|
-
|
42
|
-
loginRequired.redirect();
|
43
|
-
break;
|
44
|
-
}
|
45
|
-
default: {
|
46
|
-
fetchErrorNotification.error({ errorMessage: errorData.message, status });
|
47
|
-
}
|
48
|
-
}
|
49
|
-
});
|
50
|
-
|
51
|
-
return response;
|
52
|
-
},
|
53
|
-
headers: async () => {
|
54
|
-
// dynamic import to avoid circular dependency
|
55
|
-
const { createHeaderWithAuth } = await import('@/services/_auth');
|
56
|
-
|
57
|
-
let provider: ModelProvider = ModelProvider.OpenAI;
|
58
|
-
// for image page, we need to get the provider from the store
|
59
|
-
log('Getting provider from store for image page: %s', location.pathname);
|
60
|
-
if (location.pathname === '/image') {
|
61
|
-
const { getImageStoreState } = await import('@/store/image');
|
62
|
-
const { imageGenerationConfigSelectors } = await import(
|
63
|
-
'@/store/image/slices/generationConfig/selectors'
|
64
|
-
);
|
65
|
-
provider = imageGenerationConfigSelectors.provider(getImageStoreState()) as ModelProvider;
|
66
|
-
log('Getting provider from store for image page: %s', provider);
|
67
|
-
}
|
68
|
-
|
69
|
-
// TODO: we need to support provider select for chat page
|
70
|
-
const headers = await createHeaderWithAuth({ provider });
|
71
|
-
log('Headers: %O', headers);
|
72
|
-
return headers;
|
73
|
-
},
|
74
|
-
maxURLLength: 2083,
|
75
|
-
transformer: superjson,
|
76
|
-
url: '/trpc/lambda',
|
77
|
-
}),
|
78
|
-
];
|
90
|
+
// 3. assembly links
|
91
|
+
const links = [errorHandlingLink, customHttpBatchLink];
|
79
92
|
|
80
93
|
export const lambdaClient = createTRPCClient<LambdaRouter>({
|
81
94
|
links,
|
@@ -71,10 +71,11 @@ export default {
|
|
71
71
|
input: {
|
72
72
|
addAi: '添加一条 AI 消息',
|
73
73
|
addUser: '添加一条用户消息',
|
74
|
+
errorMsg: '消息发送失败,请检查网络后重试: {{errorMsg}}',
|
74
75
|
more: '更多',
|
75
76
|
send: '发送',
|
76
|
-
sendWithCmdEnter: '按
|
77
|
-
sendWithEnter: '按
|
77
|
+
sendWithCmdEnter: '按 <key/> 键发送',
|
78
|
+
sendWithEnter: '按 <key/> 键发送',
|
78
79
|
stop: '停止',
|
79
80
|
warp: '换行',
|
80
81
|
},
|
@@ -236,6 +237,10 @@ export default {
|
|
236
237
|
threadMessageCount: '{{messageCount}} 条消息',
|
237
238
|
title: '子话题',
|
238
239
|
},
|
240
|
+
toggleWideScreen: {
|
241
|
+
off: '关闭宽屏模式',
|
242
|
+
on: '开启宽屏模式',
|
243
|
+
},
|
239
244
|
tokenDetails: {
|
240
245
|
chats: '会话消息',
|
241
246
|
historySummary: '历史总结',
|
@@ -0,0 +1,47 @@
|
|
1
|
+
export default {
|
2
|
+
actions: {
|
3
|
+
expand: {
|
4
|
+
off: '收起',
|
5
|
+
on: '展开',
|
6
|
+
},
|
7
|
+
typobar: {
|
8
|
+
off: '隐藏格式工具栏',
|
9
|
+
on: '显示格式工具栏',
|
10
|
+
},
|
11
|
+
},
|
12
|
+
file: {
|
13
|
+
error: '错误:{{message}}',
|
14
|
+
uploading: '正在上传文件...',
|
15
|
+
},
|
16
|
+
image: {
|
17
|
+
broken: '图片损坏',
|
18
|
+
},
|
19
|
+
link: {
|
20
|
+
edit: '编辑链接',
|
21
|
+
open: '打开链接',
|
22
|
+
placeholder: '输入链接 URL',
|
23
|
+
unlink: '取消链接',
|
24
|
+
},
|
25
|
+
table: {
|
26
|
+
delete: '删除表格',
|
27
|
+
deleteColumn: '删除列',
|
28
|
+
deleteRow: '删除行',
|
29
|
+
insertColumnLeft: '在左侧插入 {{count}} 列',
|
30
|
+
insertColumnRight: '在右侧插入 {{count}} 列',
|
31
|
+
insertRowAbove: '在上方插入 {{count}} 行',
|
32
|
+
insertRowBelow: '在下方插入 {{count}} 行',
|
33
|
+
},
|
34
|
+
typobar: {
|
35
|
+
blockquote: '引用',
|
36
|
+
bold: '加粗',
|
37
|
+
bulletList: '无序列表',
|
38
|
+
code: '行内代码',
|
39
|
+
codeblock: '代码块',
|
40
|
+
italic: '斜体',
|
41
|
+
link: '链接',
|
42
|
+
numberList: '有序列表',
|
43
|
+
strikethrough: '删除线',
|
44
|
+
table: '插入表格',
|
45
|
+
underline: '下划线',
|
46
|
+
},
|
47
|
+
};
|
@@ -6,6 +6,7 @@ import color from './color';
|
|
6
6
|
import common from './common';
|
7
7
|
import components from './components';
|
8
8
|
import discover from './discover';
|
9
|
+
import editor from './editor';
|
9
10
|
import electron from './electron';
|
10
11
|
import error from './error';
|
11
12
|
import file from './file';
|
@@ -37,6 +38,7 @@ const resources = {
|
|
37
38
|
common,
|
38
39
|
components,
|
39
40
|
discover,
|
41
|
+
editor,
|
40
42
|
electron,
|
41
43
|
error,
|
42
44
|
file,
|
package/src/services/aiChat.ts
CHANGED
@@ -4,8 +4,14 @@ import { cleanObject } from '@lobechat/utils';
|
|
4
4
|
import { lambdaClient } from '@/libs/trpc/client';
|
5
5
|
|
6
6
|
class AiChatService {
|
7
|
-
sendMessageInServer = async (
|
8
|
-
|
7
|
+
sendMessageInServer = async (
|
8
|
+
params: SendMessageServerParams,
|
9
|
+
abortController: AbortController,
|
10
|
+
) => {
|
11
|
+
return lambdaClient.aiChat.sendMessageInServer.mutate(cleanObject(params), {
|
12
|
+
context: { showNotification: false },
|
13
|
+
signal: abortController?.signal,
|
14
|
+
});
|
9
15
|
};
|
10
16
|
}
|
11
17
|
|