@lobehub/chat 1.123.4 → 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.
Files changed (126) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/changelog/v1.json +9 -0
  3. package/locales/ar/chat.json +6 -2
  4. package/locales/ar/editor.json +47 -0
  5. package/locales/bg-BG/chat.json +6 -2
  6. package/locales/bg-BG/editor.json +47 -0
  7. package/locales/de-DE/chat.json +6 -2
  8. package/locales/de-DE/editor.json +47 -0
  9. package/locales/en-US/chat.json +6 -2
  10. package/locales/en-US/editor.json +47 -0
  11. package/locales/es-ES/chat.json +6 -2
  12. package/locales/es-ES/editor.json +47 -0
  13. package/locales/es-ES/models.json +3 -1
  14. package/locales/fa-IR/chat.json +6 -2
  15. package/locales/fa-IR/editor.json +47 -0
  16. package/locales/fr-FR/chat.json +6 -2
  17. package/locales/fr-FR/editor.json +47 -0
  18. package/locales/it-IT/chat.json +6 -2
  19. package/locales/it-IT/editor.json +47 -0
  20. package/locales/ja-JP/chat.json +6 -2
  21. package/locales/ja-JP/editor.json +47 -0
  22. package/locales/ko-KR/chat.json +6 -2
  23. package/locales/ko-KR/editor.json +47 -0
  24. package/locales/ko-KR/models.json +3 -1
  25. package/locales/nl-NL/chat.json +6 -2
  26. package/locales/nl-NL/editor.json +47 -0
  27. package/locales/nl-NL/models.json +3 -1
  28. package/locales/pl-PL/chat.json +6 -2
  29. package/locales/pl-PL/editor.json +47 -0
  30. package/locales/pt-BR/chat.json +6 -2
  31. package/locales/pt-BR/editor.json +47 -0
  32. package/locales/ru-RU/chat.json +6 -2
  33. package/locales/ru-RU/editor.json +47 -0
  34. package/locales/tr-TR/chat.json +6 -2
  35. package/locales/tr-TR/editor.json +47 -0
  36. package/locales/vi-VN/chat.json +6 -2
  37. package/locales/vi-VN/editor.json +47 -0
  38. package/locales/zh-CN/chat.json +6 -2
  39. package/locales/zh-CN/editor.json +47 -0
  40. package/locales/zh-TW/chat.json +6 -2
  41. package/locales/zh-TW/editor.json +47 -0
  42. package/locales/zh-TW/models.json +3 -1
  43. package/next.config.ts +4 -0
  44. package/package.json +4 -2
  45. package/packages/const/src/layoutTokens.ts +1 -0
  46. package/packages/types/src/index.ts +1 -0
  47. package/packages/utils/src/index.ts +1 -0
  48. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/{Footer/MessageFromUrl.tsx → MessageFromUrl.tsx} +3 -2
  49. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +129 -28
  50. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +44 -66
  51. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +141 -0
  52. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx +7 -1
  53. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/InboxWelcome/QuestionSuggest.tsx +3 -2
  54. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/OpeningQuestions.tsx +3 -2
  55. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +18 -2
  56. package/src/features/ChatInput/ActionBar/STT/common.tsx +41 -47
  57. package/src/features/ChatInput/{Topic → ActionBar/SaveTopic}/index.tsx +15 -4
  58. package/src/features/ChatInput/ActionBar/Typo/index.tsx +22 -0
  59. package/src/features/ChatInput/ActionBar/components/Action.tsx +4 -0
  60. package/src/features/ChatInput/ActionBar/config.ts +7 -1
  61. package/src/features/ChatInput/ActionBar/index.tsx +40 -51
  62. package/src/features/ChatInput/ChatInputProvider.tsx +54 -0
  63. package/src/features/ChatInput/Desktop/FilePreview/FileItem/index.tsx +20 -11
  64. package/src/features/ChatInput/Desktop/FilePreview/FileList.tsx +16 -15
  65. package/src/features/ChatInput/Desktop/index.tsx +81 -68
  66. package/src/features/ChatInput/InputEditor/index.tsx +134 -0
  67. package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/File.tsx +1 -2
  68. package/src/features/ChatInput/Mobile/FilePreview/index.tsx +44 -0
  69. package/src/features/ChatInput/Mobile/index.tsx +72 -0
  70. package/src/features/ChatInput/SendArea/ExpandButton.tsx +30 -0
  71. package/src/features/ChatInput/SendArea/SendButton.tsx +29 -0
  72. package/src/features/ChatInput/SendArea/ShortcutHint.tsx +52 -0
  73. package/src/features/ChatInput/SendArea/index.tsx +36 -0
  74. package/src/features/ChatInput/StoreUpdater.tsx +41 -0
  75. package/src/features/ChatInput/TypoBar/index.tsx +139 -0
  76. package/src/features/ChatInput/hooks/useChatInputEditor.ts +36 -0
  77. package/src/features/ChatInput/index.ts +7 -0
  78. package/src/features/ChatInput/store/action.ts +75 -0
  79. package/src/features/ChatInput/store/index.ts +23 -0
  80. package/src/features/ChatInput/store/initialState.ts +54 -0
  81. package/src/features/ChatInput/store/selectors.ts +5 -0
  82. package/src/features/Conversation/components/BackBottom/style.ts +1 -1
  83. package/src/features/Conversation/components/SkeletonList.tsx +10 -3
  84. package/src/features/Conversation/components/VirtualizedList/index.tsx +53 -44
  85. package/src/features/Conversation/components/WideScreenContainer/index.tsx +43 -0
  86. package/src/features/Portal/Thread/Chat/ChatInput/index.tsx +49 -42
  87. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +48 -22
  88. package/src/features/Portal/Thread/Chat/index.tsx +2 -2
  89. package/src/features/Portal/Thread/Header/index.tsx +1 -1
  90. package/src/hooks/useHotkeys/chatScope.ts +5 -3
  91. package/src/layout/GlobalProvider/Editor.tsx +27 -0
  92. package/src/layout/GlobalProvider/Locale.tsx +3 -23
  93. package/src/locales/default/chat.ts +7 -2
  94. package/src/locales/default/editor.ts +47 -0
  95. package/src/locales/default/index.ts +2 -0
  96. package/src/services/aiChat.ts +8 -2
  97. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +42 -33
  98. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +174 -35
  99. package/src/store/chat/slices/aiChat/initialState.ts +19 -0
  100. package/src/store/chat/slices/aiChat/selectors.ts +18 -0
  101. package/src/store/global/action.test.ts +6 -5
  102. package/src/store/global/actions/__tests__/general.test.ts +6 -6
  103. package/src/store/global/actions/workspacePane.ts +6 -0
  104. package/src/store/global/initialState.ts +2 -4
  105. package/src/store/global/selectors/systemStatus.test.ts +1 -2
  106. package/src/store/global/selectors/systemStatus.ts +2 -5
  107. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +0 -104
  108. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/ShortcutHint.tsx +0 -40
  109. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +0 -125
  110. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx +0 -332
  111. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx +0 -29
  112. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/index.tsx +0 -33
  113. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/Container.tsx +0 -41
  114. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/index.tsx +0 -156
  115. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Send.tsx +0 -33
  116. package/src/features/ChatInput/Desktop/Header/index.tsx +0 -30
  117. package/src/features/ChatInput/Desktop/InputArea/index.tsx +0 -143
  118. package/src/features/ChatInput/Desktop/__tests__/useAutoFocus.test.ts +0 -45
  119. package/src/features/ChatInput/Desktop/useAutoFocus.ts +0 -13
  120. package/src/features/ChatInput/useSend.ts +0 -102
  121. package/src/features/Portal/Thread/Chat/ChatInput/Footer.tsx +0 -90
  122. package/src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx +0 -30
  123. package/src/libs/trpc/client/types.ts +0 -18
  124. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/Image.tsx +0 -0
  125. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/index.tsx +0 -0
  126. /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/ActionBar/config';
9
- import DesktopChatInput, { FooterRender } from '@/features/ChatInput/Desktop';
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 Footer from './Footer';
14
- import TextArea from './TextArea';
15
+ import { useSendThreadMessage } from './useSend';
15
16
 
16
- const leftActions = ['stt', 'portalToken'] as ActionKeys[];
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 [inputHeight, hideThreadLimitAlert, updateSystemStatus] = useGlobalStore((s) => [
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
- <Alert
34
- banner
35
- closable
36
- message={
37
- <Trans i18nKey={'notSupportMultiModals'} ns={'thread'}>
38
- 子话题暂不支持文件/图片上传,如有需求,欢迎留言:
39
- <Link
40
- href={'https://github.com/lobehub/lobe-chat/discussions/4717'}
41
- style={{ textDecoration: 'underline' }}
42
- >
43
- 💬 讨论
44
- </Link>
45
- </Trans>
46
- }
47
- onClose={() => {
48
- updateSystemStatus({ hideThreadLimitAlert: true });
49
- }}
50
- type={'info'}
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
- <DesktopChatInput
54
- inputHeight={inputHeight}
55
- leftActions={leftActions}
56
- onInputHeightChange={(height) => {
57
- updateSystemStatus({ threadInputHeight: height });
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
- renderFooter={renderFooter}
60
- renderTextArea={renderTextArea}
61
- rightActions={rightActions}
62
- />
63
- </>
67
+ >
68
+ <DesktopChatInput />
69
+ </ChatInputProvider>
70
+ </WideScreenContainer>
64
71
  );
65
72
  });
66
73
 
@@ -1,5 +1,8 @@
1
- import { useCallback, useMemo } from 'react';
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 isSendButtonDisabledByMessage = useChatStore(threadSelectors.isSendButtonDisabledByMessage);
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
- const isSendButtonDisabledByMessage = threadSelectors.isSendButtonDisabledByMessage(
27
- useChatStore.getState(),
28
- );
36
+ if (!threadInputEditor) {
37
+ console.warn('not found threadInputEditor instance');
38
+ return;
39
+ }
29
40
 
30
- const canSend = !isSendButtonDisabledByMessage;
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 (!store.threadInputMessage) return;
44
+ if (!inputMessage) return;
35
45
 
36
- sendMessage({ message: store.threadInputMessage, ...params });
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
- updateInputMessage('');
55
+ if (!shouldContinue) return;
56
+
57
+ updateInputMessage(inputMessage);
39
58
 
40
- // const hasSystemRole = agentSelectors.hasSystemRole(useAgentStore.getState());
41
- // const agentSetting = useAgentStore.getState().agentSettingInstance;
59
+ sendMessage({ message: inputMessage, ...params });
60
+
61
+ updateInputMessage('');
62
+ threadInputEditor.clearContent();
63
+ threadInputEditor.focus();
64
+ };
42
65
 
43
- // // if there is a system role, then we need to use agent setting instance to autocomplete agent meta
44
- // if (hasSystemRole && !!agentSetting) {
45
- // agentSetting.autocompleteAllMeta();
46
- // }
47
- }, []);
66
+ const send = async (params: UseSendMessageParams = {}) => {
67
+ setLoading(true);
68
+ await handleSend(params);
69
+ setLoading(false);
70
+ };
48
71
 
49
- return useMemo(() => ({ canSend, send }), [canSend]);
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
- <Flexbox height={'100%'}>
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
- </Flexbox>
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
- background: `linear-gradient(to bottom, ${theme.colorBgContainerSecondary}, ${theme.colorFillQuaternary})`,
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 } = useSendMessage();
79
- return useHotkeyById(HotkeyEnum.AddUserMessage, () => send({ onlyAddUserMessage: true }));
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
  });
@@ -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: '按 {{meta}} + Enter 键发送',
77
- sendWithEnter: '按 Enter 键发送',
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,
@@ -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 (params: SendMessageServerParams) => {
8
- return lambdaClient.aiChat.sendMessageInServer.mutate(cleanObject(params));
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
 
@@ -187,18 +187,21 @@ describe('generateAIChatV2 actions', () => {
187
187
  await result.current.sendMessage({ message, files });
188
188
  });
189
189
 
190
- expect(aiChatService.sendMessageInServer).toHaveBeenCalledWith({
191
- newAssistantMessage: {
192
- model: DEFAULT_MODEL,
193
- provider: DEFAULT_PROVIDER,
190
+ expect(aiChatService.sendMessageInServer).toHaveBeenCalledWith(
191
+ {
192
+ newAssistantMessage: {
193
+ model: DEFAULT_MODEL,
194
+ provider: DEFAULT_PROVIDER,
195
+ },
196
+ newUserMessage: {
197
+ content: message,
198
+ files: files.map((f) => f.id),
199
+ },
200
+ sessionId: mockState.activeId,
201
+ topicId: mockState.activeTopicId,
194
202
  },
195
- newUserMessage: {
196
- content: message,
197
- files: files.map((f) => f.id),
198
- },
199
- sessionId: mockState.activeId,
200
- topicId: mockState.activeTopicId,
201
- });
203
+ expect.anything(),
204
+ );
202
205
  expect(result.current.internal_execAgentRuntime).toHaveBeenCalled();
203
206
  });
204
207
 
@@ -270,18 +273,21 @@ describe('generateAIChatV2 actions', () => {
270
273
  await result.current.sendMessage({ message: '', files: [{ id: 'file-1' }] as any });
271
274
  });
272
275
 
273
- expect(aiChatService.sendMessageInServer).toHaveBeenCalledWith({
274
- newAssistantMessage: {
275
- model: DEFAULT_MODEL,
276
- provider: DEFAULT_PROVIDER,
277
- },
278
- newUserMessage: {
279
- content: '',
280
- files: ['file-1'],
276
+ expect(aiChatService.sendMessageInServer).toHaveBeenCalledWith(
277
+ {
278
+ newAssistantMessage: {
279
+ model: DEFAULT_MODEL,
280
+ provider: DEFAULT_PROVIDER,
281
+ },
282
+ newUserMessage: {
283
+ content: '',
284
+ files: ['file-1'],
285
+ },
286
+ sessionId: 'session-id',
287
+ topicId: 'topic-id',
281
288
  },
282
- sessionId: 'session-id',
283
- topicId: 'topic-id',
284
- });
289
+ expect.anything(),
290
+ );
285
291
  });
286
292
 
287
293
  it('当同时有文件和消息内容时,正确发送消息并关联文件', async () => {
@@ -291,18 +297,21 @@ describe('generateAIChatV2 actions', () => {
291
297
  await result.current.sendMessage({ message: 'test', files: [{ id: 'file-1' }] as any });
292
298
  });
293
299
 
294
- expect(aiChatService.sendMessageInServer).toHaveBeenCalledWith({
295
- newAssistantMessage: {
296
- model: DEFAULT_MODEL,
297
- provider: DEFAULT_PROVIDER,
298
- },
299
- newUserMessage: {
300
- content: 'test',
301
- files: ['file-1'],
300
+ expect(aiChatService.sendMessageInServer).toHaveBeenCalledWith(
301
+ {
302
+ newAssistantMessage: {
303
+ model: DEFAULT_MODEL,
304
+ provider: DEFAULT_PROVIDER,
305
+ },
306
+ newUserMessage: {
307
+ content: 'test',
308
+ files: ['file-1'],
309
+ },
310
+ sessionId: 'session-id',
311
+ topicId: 'topic-id',
302
312
  },
303
- sessionId: 'session-id',
304
- topicId: 'topic-id',
305
- });
313
+ expect.anything(),
314
+ );
306
315
  });
307
316
 
308
317
  it('当 createMessage 抛出错误时,正确处理错误而不影响整个应用', async () => {