@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.
Files changed (137) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -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/(backend)/webapi/chat/[provider]/route.ts +1 -1
  49. package/src/app/(backend)/webapi/chat/vertexai/route.ts +1 -0
  50. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/{Footer/MessageFromUrl.tsx → MessageFromUrl.tsx} +3 -2
  51. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx +129 -28
  52. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/index.tsx +44 -66
  53. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +141 -0
  54. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/Content.tsx +7 -1
  55. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/InboxWelcome/QuestionSuggest.tsx +3 -2
  56. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/OpeningQuestions.tsx +3 -2
  57. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/ChatHeader/HeaderAction.tsx +18 -2
  58. package/src/features/ChatInput/ActionBar/STT/common.tsx +41 -47
  59. package/src/features/ChatInput/{Topic → ActionBar/SaveTopic}/index.tsx +15 -4
  60. package/src/features/ChatInput/ActionBar/Typo/index.tsx +22 -0
  61. package/src/features/ChatInput/ActionBar/components/Action.tsx +4 -0
  62. package/src/features/ChatInput/ActionBar/config.ts +7 -1
  63. package/src/features/ChatInput/ActionBar/index.tsx +40 -51
  64. package/src/features/ChatInput/ChatInputProvider.tsx +54 -0
  65. package/src/features/ChatInput/Desktop/FilePreview/FileItem/index.tsx +20 -11
  66. package/src/features/ChatInput/Desktop/FilePreview/FileList.tsx +16 -15
  67. package/src/features/ChatInput/Desktop/index.tsx +81 -68
  68. package/src/features/ChatInput/InputEditor/index.tsx +134 -0
  69. package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/File.tsx +1 -2
  70. package/src/features/ChatInput/Mobile/FilePreview/index.tsx +44 -0
  71. package/src/features/ChatInput/Mobile/index.tsx +72 -0
  72. package/src/features/ChatInput/SendArea/ExpandButton.tsx +30 -0
  73. package/src/features/ChatInput/SendArea/SendButton.tsx +29 -0
  74. package/src/features/ChatInput/SendArea/ShortcutHint.tsx +52 -0
  75. package/src/features/ChatInput/SendArea/index.tsx +36 -0
  76. package/src/features/ChatInput/StoreUpdater.tsx +41 -0
  77. package/src/features/ChatInput/TypoBar/index.tsx +139 -0
  78. package/src/features/ChatInput/hooks/useChatInputEditor.ts +36 -0
  79. package/src/features/ChatInput/index.ts +7 -0
  80. package/src/features/ChatInput/store/action.ts +75 -0
  81. package/src/features/ChatInput/store/index.ts +23 -0
  82. package/src/features/ChatInput/store/initialState.ts +54 -0
  83. package/src/features/ChatInput/store/selectors.ts +5 -0
  84. package/src/features/Conversation/components/BackBottom/style.ts +1 -1
  85. package/src/features/Conversation/components/SkeletonList.tsx +10 -3
  86. package/src/features/Conversation/components/VirtualizedList/index.tsx +53 -44
  87. package/src/features/Conversation/components/WideScreenContainer/index.tsx +43 -0
  88. package/src/features/Portal/Thread/Chat/ChatInput/index.tsx +49 -42
  89. package/src/features/Portal/Thread/Chat/ChatInput/useSend.ts +48 -22
  90. package/src/features/Portal/Thread/Chat/index.tsx +2 -2
  91. package/src/features/Portal/Thread/Header/index.tsx +1 -1
  92. package/src/hooks/useHotkeys/chatScope.ts +5 -3
  93. package/src/layout/GlobalProvider/Editor.tsx +27 -0
  94. package/src/layout/GlobalProvider/Locale.tsx +3 -23
  95. package/src/libs/trpc/client/lambda.ts +76 -63
  96. package/src/locales/default/chat.ts +7 -2
  97. package/src/locales/default/editor.ts +47 -0
  98. package/src/locales/default/index.ts +2 -0
  99. package/src/services/aiChat.ts +8 -2
  100. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChatV2.test.ts +42 -33
  101. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +174 -35
  102. package/src/store/chat/slices/aiChat/initialState.ts +19 -0
  103. package/src/store/chat/slices/aiChat/selectors.ts +18 -0
  104. package/src/store/global/action.test.ts +6 -5
  105. package/src/store/global/actions/__tests__/general.test.ts +6 -6
  106. package/src/store/global/actions/workspacePane.ts +6 -0
  107. package/src/store/global/initialState.ts +2 -4
  108. package/src/store/global/selectors/systemStatus.test.ts +1 -2
  109. package/src/store/global/selectors/systemStatus.ts +2 -5
  110. package/src/app/(backend)/webapi/chat/anthropic/route.test.ts +0 -30
  111. package/src/app/(backend)/webapi/chat/anthropic/route.ts +0 -21
  112. package/src/app/(backend)/webapi/chat/google/route.test.ts +0 -35
  113. package/src/app/(backend)/webapi/chat/google/route.ts +0 -25
  114. package/src/app/(backend)/webapi/chat/groq/route.test.ts +0 -29
  115. package/src/app/(backend)/webapi/chat/groq/route.ts +0 -21
  116. package/src/app/(backend)/webapi/chat/openai/route.test.ts +0 -30
  117. package/src/app/(backend)/webapi/chat/openai/route.ts +0 -26
  118. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/SendMore.tsx +0 -104
  119. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/ShortcutHint.tsx +0 -40
  120. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +0 -125
  121. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx +0 -332
  122. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx +0 -29
  123. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files/index.tsx +0 -33
  124. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/Container.tsx +0 -41
  125. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/InputArea/index.tsx +0 -156
  126. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Send.tsx +0 -33
  127. package/src/features/ChatInput/Desktop/Header/index.tsx +0 -30
  128. package/src/features/ChatInput/Desktop/InputArea/index.tsx +0 -143
  129. package/src/features/ChatInput/Desktop/__tests__/useAutoFocus.test.ts +0 -45
  130. package/src/features/ChatInput/Desktop/useAutoFocus.ts +0 -13
  131. package/src/features/ChatInput/useSend.ts +0 -102
  132. package/src/features/Portal/Thread/Chat/ChatInput/Footer.tsx +0 -90
  133. package/src/features/Portal/Thread/Chat/ChatInput/TextArea.tsx +0 -30
  134. package/src/libs/trpc/client/types.ts +0 -18
  135. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/Image.tsx +0 -0
  136. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/index.tsx +0 -0
  137. /package/src/{app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/Mobile/Files → features/ChatInput/Mobile/FilePreview}/FileItem/style.ts +0 -0
@@ -1,21 +0,0 @@
1
- import { POST as UniverseRoute } from '../[provider]/route';
2
-
3
- export const runtime = 'edge';
4
-
5
- export const preferredRegion = [
6
- 'bom1',
7
- 'cle1',
8
- 'cpt1',
9
- 'gru1',
10
- 'hnd1',
11
- 'iad1',
12
- 'icn1',
13
- 'kix1',
14
- 'pdx1',
15
- 'sfo1',
16
- 'sin1',
17
- 'syd1',
18
- ];
19
-
20
- export const POST = async (req: Request) =>
21
- UniverseRoute(req, { params: Promise.resolve({ provider: 'anthropic' }) });
@@ -1,35 +0,0 @@
1
- // @vitest-environment edge-runtime
2
- import { describe, expect, it, vi } from 'vitest';
3
-
4
- import { POST as UniverseRoute } from '../[provider]/route';
5
- import { POST, preferredRegion, runtime } from './route';
6
-
7
- // 模拟 '../[provider]/route'
8
- vi.mock('../[provider]/route', () => ({
9
- POST: vi.fn().mockResolvedValue('mocked response'),
10
- }));
11
-
12
- describe('Configuration tests', () => {
13
- it('should have runtime set to "edge"', () => {
14
- expect(runtime).toBe('edge');
15
- });
16
-
17
- it('should contain specific regions in preferredRegion', () => {
18
- expect(preferredRegion).not.contain(['hkg1']);
19
- expect(preferredRegion).not.contain(['dub1']);
20
- expect(preferredRegion).not.contain(['cdg1']);
21
- expect(preferredRegion).not.contain(['fra1']);
22
- expect(preferredRegion).not.contain(['lhr1']);
23
- expect(preferredRegion).not.contain(['arn1']);
24
- });
25
- });
26
-
27
- describe('Google POST function tests', () => {
28
- it('should call UniverseRoute with correct parameters', async () => {
29
- const mockRequest = new Request('https://example.com', { method: 'POST' });
30
- await POST(mockRequest);
31
- expect(UniverseRoute).toHaveBeenCalledWith(mockRequest, {
32
- params: Promise.resolve({ provider: 'google' }),
33
- });
34
- });
35
- });
@@ -1,25 +0,0 @@
1
- import { POST as UniverseRoute } from '../[provider]/route';
2
-
3
- export const runtime = 'edge';
4
-
5
- // due to Gemini-1.5-pro is not available in Hong Kong, we need to set the preferred region to exclude "Hong Kong (hkg1)".
6
- // the paid service of the Gemini API is required in the following regions until 8 July 2024. The free service is not available. Therefore, the regions is temporarily disabled.
7
- // regions include Dublin (dub1, Ireland), Paris (cdg1, France), Frankfurt (fra1, Germany), London (lhr1, UK), and Stockholm (arn1, Sweden).
8
- // refs: https://ai.google.dev/gemini-api/docs/available-regions
9
- export const preferredRegion = [
10
- 'icn1',
11
- 'sin1',
12
- 'hnd1',
13
- 'kix1',
14
- 'bom1',
15
- 'cpt1',
16
- 'pdx1',
17
- 'cle1',
18
- 'syd1',
19
- 'iad1',
20
- 'sfo1',
21
- 'gru1',
22
- ];
23
-
24
- export const POST = async (req: Request) =>
25
- UniverseRoute(req, { params: Promise.resolve({ provider: 'google' }) });
@@ -1,29 +0,0 @@
1
- // @vitest-environment edge-runtime
2
- import { describe, expect, it, vi } from 'vitest';
3
-
4
- import { POST as UniverseRoute } from '../[provider]/route';
5
- import { POST, preferredRegion, runtime } from './route';
6
-
7
- vi.mock('../[provider]/route', () => ({
8
- POST: vi.fn().mockResolvedValue('mocked response'),
9
- }));
10
-
11
- describe('Configuration tests', () => {
12
- it('should have runtime set to "edge"', () => {
13
- expect(runtime).toBe('edge');
14
- });
15
-
16
- it('should contain specific regions in preferredRegion', () => {
17
- expect(preferredRegion).not.contain(['hk1']);
18
- });
19
- });
20
-
21
- describe('Groq POST function tests', () => {
22
- it('should call UniverseRoute with correct parameters', async () => {
23
- const mockRequest = new Request('https://example.com', { method: 'POST' });
24
- await POST(mockRequest);
25
- expect(UniverseRoute).toHaveBeenCalledWith(mockRequest, {
26
- params: Promise.resolve({ provider: 'groq' }),
27
- });
28
- });
29
- });
@@ -1,21 +0,0 @@
1
- import { POST as UniverseRoute } from '../[provider]/route';
2
-
3
- export const runtime = 'edge';
4
-
5
- export const preferredRegion = [
6
- 'bom1',
7
- 'cle1',
8
- 'cpt1',
9
- 'gru1',
10
- 'hnd1',
11
- 'iad1',
12
- 'icn1',
13
- 'kix1',
14
- 'pdx1',
15
- 'sfo1',
16
- 'sin1',
17
- 'syd1',
18
- ];
19
-
20
- export const POST = async (req: Request) =>
21
- UniverseRoute(req, { params: Promise.resolve({ provider: 'groq' }) });
@@ -1,30 +0,0 @@
1
- // @vitest-environment edge-runtime
2
- import { describe, expect, it, vi } from 'vitest';
3
-
4
- import { POST as UniverseRoute } from '../[provider]/route';
5
- import { POST, preferredRegion, runtime } from './route';
6
-
7
- // 模拟 '../[provider]/route'
8
- vi.mock('../[provider]/route', () => ({
9
- POST: vi.fn().mockResolvedValue('mocked response'),
10
- }));
11
-
12
- describe('Configuration tests', () => {
13
- it('should have runtime set to "edge"', () => {
14
- expect(runtime).toBe('edge');
15
- });
16
-
17
- it('should contain specific regions in preferredRegion', () => {
18
- expect(preferredRegion).not.contain(['hkg1']);
19
- });
20
- });
21
-
22
- describe('OpenAI POST function tests', () => {
23
- it('should call UniverseRoute with correct parameters', async () => {
24
- const mockRequest = new Request('https://example.com', { method: 'POST' });
25
- await POST(mockRequest);
26
- expect(UniverseRoute).toHaveBeenCalledWith(mockRequest, {
27
- params: Promise.resolve({ provider: 'openai' }),
28
- });
29
- });
30
- });
@@ -1,26 +0,0 @@
1
- import { POST as UniverseRoute } from '../[provider]/route';
2
-
3
- export const runtime = 'edge';
4
-
5
- export const preferredRegion = [
6
- 'arn1',
7
- 'bom1',
8
- 'cdg1',
9
- 'cle1',
10
- 'cpt1',
11
- 'dub1',
12
- 'fra1',
13
- 'gru1',
14
- 'hnd1',
15
- 'iad1',
16
- 'icn1',
17
- 'kix1',
18
- 'lhr1',
19
- 'pdx1',
20
- 'sfo1',
21
- 'sin1',
22
- 'syd1',
23
- ];
24
-
25
- export const POST = async (req: Request) =>
26
- UniverseRoute(req, { params: Promise.resolve({ provider: 'openai' }) });
@@ -1,104 +0,0 @@
1
- import { Button, Dropdown, Hotkey, Icon } from '@lobehub/ui';
2
- import { createStyles } from 'antd-style';
3
- import { BotMessageSquare, LucideCheck, LucideChevronDown, MessageSquarePlus } from 'lucide-react';
4
- import { memo } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
- import { Flexbox } from 'react-layout-kit';
7
-
8
- import { useSendMessage } from '@/features/ChatInput/useSend';
9
- import { useChatStore } from '@/store/chat';
10
- import { useUserStore } from '@/store/user';
11
- import { preferenceSelectors, settingsSelectors } from '@/store/user/selectors';
12
- import { HotkeyEnum } from '@/types/hotkey';
13
-
14
- const useStyles = createStyles(({ css, prefixCls }) => {
15
- return {
16
- arrow: css`
17
- &.${prefixCls}-btn.${prefixCls}-btn-icon-only {
18
- width: 28px;
19
- }
20
- `,
21
- };
22
- });
23
-
24
- interface SendMoreProps {
25
- disabled?: boolean;
26
- isMac?: boolean;
27
- }
28
-
29
- const SendMore = memo<SendMoreProps>(({ disabled, isMac }) => {
30
- const { t } = useTranslation('chat');
31
- const hotkey = useUserStore(settingsSelectors.getHotkeyById(HotkeyEnum.AddUserMessage));
32
- const { styles } = useStyles();
33
-
34
- const [useCmdEnterToSend, updatePreference] = useUserStore((s) => [
35
- preferenceSelectors.useCmdEnterToSend(s),
36
- s.updatePreference,
37
- ]);
38
- const addAIMessage = useChatStore((s) => s.addAIMessage);
39
-
40
- const { send: sendMessage } = useSendMessage();
41
-
42
- return (
43
- <Dropdown
44
- disabled={disabled}
45
- menu={{
46
- items: [
47
- {
48
- icon: !useCmdEnterToSend ? <Icon icon={LucideCheck} /> : <div />,
49
- key: 'sendWithEnter',
50
- label: t('input.sendWithEnter'),
51
- onClick: () => {
52
- updatePreference({ useCmdEnterToSend: false });
53
- },
54
- },
55
- {
56
- icon: useCmdEnterToSend ? <Icon icon={LucideCheck} /> : <div />,
57
- key: 'sendWithCmdEnter',
58
- label: t('input.sendWithCmdEnter', {
59
- meta: typeof isMac === 'boolean' ? (isMac ? '⌘' : 'Ctrl') : '…',
60
- }),
61
- onClick: () => {
62
- updatePreference({ useCmdEnterToSend: true });
63
- },
64
- },
65
- { type: 'divider' },
66
- {
67
- icon: <Icon icon={BotMessageSquare} />,
68
- key: 'addAi',
69
- label: t('input.addAi'),
70
- onClick: () => {
71
- addAIMessage();
72
- },
73
- },
74
- {
75
- icon: <Icon icon={MessageSquarePlus} />,
76
- key: 'addUser',
77
- label: (
78
- <Flexbox align={'center'} gap={24} horizontal>
79
- {t('input.addUser')}
80
- <Hotkey keys={hotkey} />
81
- </Flexbox>
82
- ),
83
- onClick: () => {
84
- sendMessage({ onlyAddUserMessage: true });
85
- },
86
- },
87
- ],
88
- }}
89
- placement={'topRight'}
90
- trigger={['hover']}
91
- >
92
- <Button
93
- aria-label={t('input.more')}
94
- className={styles.arrow}
95
- icon={LucideChevronDown}
96
- type={'primary'}
97
- />
98
- </Dropdown>
99
- );
100
- });
101
-
102
- SendMore.displayName = 'SendMore';
103
-
104
- export default SendMore;
@@ -1,40 +0,0 @@
1
- import { Hotkey, combineKeys } from '@lobehub/ui';
2
- import { useTheme } from 'antd-style';
3
- import { memo } from 'react';
4
- import { useTranslation } from 'react-i18next';
5
- import { Flexbox } from 'react-layout-kit';
6
-
7
- import { useUserStore } from '@/store/user';
8
- import { preferenceSelectors } from '@/store/user/selectors';
9
- import { KeyEnum } from '@/types/hotkey';
10
-
11
- const ShortcutHint = memo(() => {
12
- const { t } = useTranslation('chat');
13
- const theme = useTheme();
14
- const useCmdEnterToSend = useUserStore(preferenceSelectors.useCmdEnterToSend);
15
-
16
- const sendShortcut = useCmdEnterToSend
17
- ? combineKeys([KeyEnum.Mod, KeyEnum.Enter])
18
- : KeyEnum.Enter;
19
-
20
- const wrapperShortcut = useCmdEnterToSend
21
- ? KeyEnum.Enter
22
- : combineKeys([KeyEnum.Mod, KeyEnum.Enter]);
23
-
24
- return (
25
- <Flexbox
26
- align={'center'}
27
- gap={4}
28
- horizontal
29
- style={{ color: theme.colorTextDescription, fontSize: 12, marginRight: 12 }}
30
- >
31
- <Hotkey keys={sendShortcut} style={{ color: 'inherit' }} variant={'borderless'} />
32
- <span>{t('input.send')}</span>
33
- <span>/</span>
34
- <Hotkey keys={wrapperShortcut} style={{ color: 'inherit' }} variant={'borderless'} />
35
- <span>{t('input.warp')}</span>
36
- </Flexbox>
37
- );
38
- });
39
-
40
- export default ShortcutHint;
@@ -1,125 +0,0 @@
1
- import { Button } from '@lobehub/ui';
2
- import { Space } from 'antd';
3
- import { createStyles } from 'antd-style';
4
- import { rgba } from 'polished';
5
- import { Suspense, memo, useEffect, useState } from 'react';
6
- import { useTranslation } from 'react-i18next';
7
- import { Flexbox } from 'react-layout-kit';
8
-
9
- import StopLoadingIcon from '@/components/StopLoading';
10
- import LocalFiles from '@/features/ChatInput/Desktop/FilePreview';
11
- import SaveTopic from '@/features/ChatInput/Topic';
12
- import { useSendMessage } from '@/features/ChatInput/useSend';
13
- import { useChatStore } from '@/store/chat';
14
- import { chatSelectors } from '@/store/chat/selectors';
15
- import { isMacOS } from '@/utils/platform';
16
-
17
- import MessageFromUrl from './MessageFromUrl';
18
- import SendMore from './SendMore';
19
- import ShortcutHint from './ShortcutHint';
20
-
21
- const useStyles = createStyles(({ css, prefixCls, token }) => {
22
- return {
23
- arrow: css`
24
- &.${prefixCls}-btn.${prefixCls}-btn-icon-only {
25
- width: 28px;
26
- }
27
- `,
28
- loadingButton: css`
29
- display: flex;
30
- align-items: center;
31
- `,
32
- overrideAntdIcon: css`
33
- .${prefixCls}-btn.${prefixCls}-btn-icon-only {
34
- display: flex;
35
- align-items: center;
36
- justify-content: center;
37
- }
38
-
39
- .${prefixCls}-btn.${prefixCls}-dropdown-trigger {
40
- &::before {
41
- background-color: ${rgba(token.colorBgLayout, 0.1)} !important;
42
- }
43
- }
44
- `,
45
- };
46
- });
47
-
48
- interface FooterProps {
49
- expand: boolean;
50
- onExpandChange: (expand: boolean) => void;
51
- }
52
-
53
- const Footer = memo<FooterProps>(({ onExpandChange, expand }) => {
54
- const { t } = useTranslation('chat');
55
-
56
- const { styles } = useStyles();
57
-
58
- const [isAIGenerating, stopGenerateMessage] = useChatStore((s) => [
59
- chatSelectors.isAIGenerating(s),
60
- s.stopGenerateMessage,
61
- ]);
62
-
63
- const { send: sendMessage, canSend } = useSendMessage();
64
-
65
- const [isMac, setIsMac] = useState<boolean>();
66
-
67
- useEffect(() => {
68
- setIsMac(isMacOS());
69
- }, [setIsMac]);
70
-
71
- return (
72
- <>
73
- <Suspense>
74
- <MessageFromUrl />
75
- </Suspense>
76
- <Flexbox
77
- align={'end'}
78
- className={styles.overrideAntdIcon}
79
- distribution={'space-between'}
80
- flex={'none'}
81
- gap={8}
82
- horizontal
83
- paddingInline={16}
84
- >
85
- <Flexbox align={'center'} gap={8} horizontal style={{ overflow: 'hidden' }}>
86
- {expand && <LocalFiles />}
87
- </Flexbox>
88
- <Flexbox align={'center'} flex={'none'} gap={8} horizontal>
89
- <ShortcutHint />
90
- <SaveTopic />
91
- <Flexbox style={{ minWidth: 92 }}>
92
- {isAIGenerating ? (
93
- <Button
94
- className={styles.loadingButton}
95
- icon={StopLoadingIcon}
96
- onClick={stopGenerateMessage}
97
- >
98
- {t('input.stop')}
99
- </Button>
100
- ) : (
101
- <Space.Compact>
102
- <Button
103
- disabled={!canSend}
104
- loading={!canSend}
105
- onClick={() => {
106
- sendMessage();
107
- onExpandChange?.(false);
108
- }}
109
- type={'primary'}
110
- >
111
- {t('input.send')}
112
- </Button>
113
- <SendMore disabled={!canSend} isMac={isMac} />
114
- </Space.Compact>
115
- )}
116
- </Flexbox>
117
- </Flexbox>
118
- </Flexbox>
119
- </>
120
- );
121
- });
122
-
123
- Footer.displayName = 'Footer';
124
-
125
- export default Footer;