@lobehub/chat 0.100.4 → 0.101.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 (83) hide show
  1. package/.eslintrc.js +1 -0
  2. package/CHANGELOG.md +50 -0
  3. package/README.md +2 -2
  4. package/README.zh-CN.md +2 -2
  5. package/__mocks__/zustand/traditional.ts +25 -0
  6. package/package.json +7 -3
  7. package/src/app/api/files/image/imgur.ts +72 -0
  8. package/src/app/api/files/image/route.ts +42 -0
  9. package/src/app/api/openai/chat/createChatCompletion.ts +2 -1
  10. package/src/app/chat/(desktop)/features/ChatInput/DragUpload.tsx +166 -0
  11. package/src/app/chat/(desktop)/features/ChatInput/Footer/LocalFiles.tsx +10 -0
  12. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +64 -0
  13. package/src/app/chat/(desktop)/features/ChatInput/InputArea.tsx +69 -0
  14. package/src/app/chat/(desktop)/features/ChatInput/index.tsx +60 -19
  15. package/src/app/chat/(mobile)/features/ChatInput/Mobile.tsx +79 -0
  16. package/src/app/chat/(mobile)/features/{ChatInput.tsx → ChatInput/index.tsx} +19 -3
  17. package/src/app/chat/(mobile)/features/ChatInput/style.mobile.ts +19 -0
  18. package/src/app/chat/components/FileList/FileItem.style.ts +81 -0
  19. package/src/app/chat/components/FileList/FileItem.tsx +69 -0
  20. package/src/app/chat/components/FileList/Lightbox.tsx +46 -0
  21. package/src/app/chat/components/FileList/index.tsx +63 -0
  22. package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +48 -0
  23. package/src/app/chat/features/ChatInput/ActionBar/FileUpload.tsx +53 -0
  24. package/src/app/chat/features/ChatInput/ActionBar/History.tsx +64 -0
  25. package/src/app/chat/features/ChatInput/ActionBar/ModelSwitch.tsx +42 -0
  26. package/src/app/chat/features/ChatInput/ActionBar/Temperature.tsx +47 -0
  27. package/src/app/chat/features/{ChatInputContent/ActionBar/Token.tsx → ChatInput/ActionBar/Token/TokenTag.tsx} +4 -4
  28. package/src/app/chat/features/ChatInput/ActionBar/Token/index.tsx +21 -0
  29. package/src/app/chat/features/ChatInput/ActionBar/config.ts +21 -0
  30. package/src/app/chat/features/ChatInput/ActionBar/index.tsx +35 -0
  31. package/src/app/chat/features/ChatInput/useSend.ts +21 -0
  32. package/src/app/chat/features/Conversation/ChatList/Messages/User.tsx +23 -0
  33. package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +2 -0
  34. package/src/config/__tests__/client.test.ts +76 -0
  35. package/src/config/__tests__/server.test.ts +52 -0
  36. package/src/config/server.ts +8 -0
  37. package/src/const/layoutTokens.ts +1 -1
  38. package/src/const/llm.ts +3 -0
  39. package/src/const/settings.ts +2 -0
  40. package/src/database/core/__tests__/db.test.ts +77 -0
  41. package/src/database/core/__tests__/model.test.ts +55 -0
  42. package/src/database/core/db.ts +25 -0
  43. package/src/database/core/index.ts +1 -0
  44. package/src/database/core/model.ts +49 -0
  45. package/src/database/core/schema.ts +9 -0
  46. package/src/database/models/file.test.ts +71 -0
  47. package/src/database/models/file.ts +26 -0
  48. package/src/locales/default/chat.ts +5 -0
  49. package/src/services/__tests__/file.test.ts +88 -0
  50. package/src/services/chatModel.ts +11 -6
  51. package/src/services/file.ts +36 -0
  52. package/src/store/files/index.ts +2 -0
  53. package/src/store/files/initialState.ts +7 -0
  54. package/src/store/files/selectors.test.ts +75 -0
  55. package/src/store/files/selectors.ts +34 -0
  56. package/src/store/files/slices/images/action.test.ts +186 -0
  57. package/src/store/files/slices/images/action.ts +83 -0
  58. package/src/store/files/slices/images/index.ts +3 -0
  59. package/src/store/files/slices/images/initialState.ts +11 -0
  60. package/src/store/files/store.ts +27 -0
  61. package/src/store/global/selectors/settings.test.ts +1 -3
  62. package/src/store/session/slices/agentConfig/selectors.ts +7 -1
  63. package/src/store/session/slices/chat/actions/message.ts +83 -18
  64. package/src/store/session/slices/chat/initialState.ts +4 -1
  65. package/src/store/session/slices/chat/reducers/files.test.ts +38 -0
  66. package/src/store/session/slices/chat/reducers/files.ts +37 -0
  67. package/src/store/session/slices/chat/selectors/chat.ts +3 -2
  68. package/src/store/session/slices/session/initialState.ts +1 -0
  69. package/src/store/session/slices/session/reducers/session.test.ts +98 -1
  70. package/src/store/session/slices/session/reducers/session.ts +26 -1
  71. package/src/types/chatMessage.ts +3 -2
  72. package/src/types/database/db.ts +4 -0
  73. package/src/types/database/files.ts +38 -0
  74. package/src/types/files.ts +8 -0
  75. package/src/types/openai/chat.ts +15 -1
  76. package/src/types/session.ts +1 -0
  77. package/tests/setup.ts +8 -0
  78. package/vitest.config.ts +2 -0
  79. package/src/app/chat/(desktop)/features/ChatInput/Footer.tsx +0 -35
  80. package/src/app/chat/features/ChatInputContent/ActionBar/ActionLeft.tsx +0 -117
  81. package/src/app/chat/features/ChatInputContent/ActionBar/ActionRight.tsx +0 -48
  82. package/src/app/chat/features/ChatInputContent/index.tsx +0 -71
  83. /package/src/app/chat/features/{ChatInputContent → ChatInput}/Topic/index.tsx +0 -0
package/tests/setup.ts ADDED
@@ -0,0 +1,8 @@
1
+ import '@testing-library/jest-dom';
2
+ // remove antd hash on test
3
+ import { theme } from 'antd';
4
+ // mock indexedDB to test with dexie
5
+ // refs: https://github.com/dumbmatter/fakeIndexedDB#dexie-and-other-indexeddb-api-wrappers
6
+ import 'fake-indexeddb/auto';
7
+
8
+ theme.defaultConfig.hashed = false;
package/vitest.config.ts CHANGED
@@ -7,10 +7,12 @@ export default defineConfig({
7
7
  '@': resolve(__dirname, './src'),
8
8
  },
9
9
  coverage: {
10
+ exclude: ['__mocks__/**'],
10
11
  provider: 'v8',
11
12
  reporter: ['text', 'json', 'lcov', 'text-summary'],
12
13
  },
13
14
  environment: 'jsdom',
14
15
  globals: true,
16
+ setupFiles: './tests/setup.ts',
15
17
  },
16
18
  });
@@ -1,35 +0,0 @@
1
- import { Icon } from '@lobehub/ui';
2
- import { useTheme } from 'antd-style';
3
- import { ArrowBigUp, CornerDownLeft } from 'lucide-react';
4
- import { memo } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
- import { Flexbox } from 'react-layout-kit';
7
-
8
- import SaveTopic from '../../../features/ChatInputContent/Topic';
9
-
10
- const Footer = memo(() => {
11
- const theme = useTheme();
12
- const { t } = useTranslation('chat');
13
-
14
- return (
15
- <>
16
- <Flexbox
17
- gap={4}
18
- horizontal
19
- style={{ color: theme.colorTextDescription, fontSize: 12, marginRight: 12 }}
20
- >
21
- <Icon icon={CornerDownLeft} />
22
- <span>{t('send')}</span>
23
- <span>/</span>
24
- <Flexbox horizontal>
25
- <Icon icon={ArrowBigUp} />
26
- <Icon icon={CornerDownLeft} />
27
- </Flexbox>
28
- <span>{t('warp')}</span>
29
- </Flexbox>
30
- <SaveTopic />
31
- </>
32
- );
33
- });
34
-
35
- export default Footer;
@@ -1,117 +0,0 @@
1
- import { ActionIcon, SliderWithInput } from '@lobehub/ui';
2
- import { Dropdown, Popover, Switch } from 'antd';
3
- import { BrainCog, Thermometer, Timer, TimerOff } from 'lucide-react';
4
- import { memo } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
- import { Flexbox } from 'react-layout-kit';
7
-
8
- import { settingsSelectors, useGlobalStore } from '@/store/global';
9
- import { useSessionStore } from '@/store/session';
10
- import { agentSelectors } from '@/store/session/selectors';
11
- import { LanguageModel } from '@/types/llm';
12
-
13
- const ActionLeft = memo(() => {
14
- const { t } = useTranslation('setting');
15
-
16
- const [model, temperature, historyCount, unlimited, updateAgentConfig] = useSessionStore((s) => {
17
- const config = agentSelectors.currentAgentConfig(s);
18
- return [
19
- config.model,
20
- config.params.temperature,
21
- config.historyCount,
22
- !config.enableHistoryCount,
23
- s.updateAgentConfig,
24
- ];
25
- });
26
-
27
- const modelList = useGlobalStore(settingsSelectors.modelList);
28
-
29
- return (
30
- <>
31
- <Dropdown
32
- menu={{
33
- activeKey: model,
34
- items: modelList.map((i) => ({ key: i, label: i })),
35
- onClick: (e) => {
36
- updateAgentConfig({ model: e.key as LanguageModel });
37
- },
38
- style: {
39
- maxHeight: 400,
40
- overflow: 'scroll',
41
- },
42
- }}
43
- trigger={['click']}
44
- >
45
- <ActionIcon icon={BrainCog} placement={'bottom'} title={t('settingModel.model.title')} />
46
- </Dropdown>
47
- <Popover
48
- arrow={false}
49
- content={
50
- <SliderWithInput
51
- controls={false}
52
- max={1}
53
- min={0}
54
- onChange={(v) => {
55
- updateAgentConfig({ params: { temperature: v } });
56
- }}
57
- size={'small'}
58
- step={0.1}
59
- style={{ width: 160 }}
60
- value={temperature}
61
- />
62
- }
63
- placement={'top'}
64
- trigger={'click'}
65
- >
66
- <ActionIcon
67
- icon={Thermometer}
68
- placement={'bottom'}
69
- title={t('settingModel.temperature.titleWithValue', { value: temperature })}
70
- />
71
- </Popover>
72
- <Popover
73
- arrow={false}
74
- content={
75
- <Flexbox align={'center'} gap={16} horizontal>
76
- <SliderWithInput
77
- disabled={unlimited}
78
- max={30}
79
- min={1}
80
- onChange={(v) => {
81
- updateAgentConfig({ historyCount: v });
82
- }}
83
- step={1}
84
- style={{ width: 160 }}
85
- value={historyCount}
86
- />
87
- <Flexbox align={'center'} gap={4} horizontal>
88
- <Switch
89
- checked={unlimited}
90
- onChange={(checked) => {
91
- updateAgentConfig({ enableHistoryCount: !checked });
92
- }}
93
- size={'small'}
94
- />
95
- {t('settingChat.enableHistoryCount.alias')}
96
- </Flexbox>
97
- </Flexbox>
98
- }
99
- placement={'top'}
100
- trigger={'click'}
101
- >
102
- <ActionIcon
103
- icon={unlimited ? TimerOff : Timer}
104
- placement={'bottom'}
105
- title={t(
106
- unlimited
107
- ? 'settingChat.enableHistoryCount.unlimited'
108
- : 'settingChat.enableHistoryCount.limited',
109
- { number: historyCount || 0 },
110
- )}
111
- />
112
- </Popover>
113
- </>
114
- );
115
- });
116
-
117
- export default ActionLeft;
@@ -1,48 +0,0 @@
1
- import { ActionIcon } from '@lobehub/ui';
2
- import { Popconfirm } from 'antd';
3
- import { useResponsive } from 'antd-style';
4
- import { Eraser } from 'lucide-react';
5
- import { memo } from 'react';
6
- import { useHotkeys } from 'react-hotkeys-hook';
7
- import { useTranslation } from 'react-i18next';
8
-
9
- import HotKeys from '@/components/HotKeys';
10
- import { CLEAN_MESSAGE_KEY, PREFIX_KEY } from '@/const/hotkeys';
11
- import { useSessionStore } from '@/store/session';
12
-
13
- import SaveTopic from '../Topic';
14
-
15
- const ActionsRight = memo(() => {
16
- const { t } = useTranslation('setting');
17
- const [clearMessage] = useSessionStore((s) => [s.clearMessage, s.updateAgentConfig]);
18
- const { mobile } = useResponsive();
19
- const hotkeys = [PREFIX_KEY, CLEAN_MESSAGE_KEY].join('+');
20
-
21
- useHotkeys(hotkeys, clearMessage, {
22
- preventDefault: true,
23
- });
24
-
25
- return (
26
- <>
27
- {mobile && <SaveTopic />}
28
- <Popconfirm
29
- cancelText={t('cancel', { ns: 'common' })}
30
- okButtonProps={{ danger: true }}
31
- okText={t('ok', { ns: 'common' })}
32
- onConfirm={() => clearMessage()}
33
- placement={'topRight'}
34
- title={t('confirmClearCurrentMessages', { ns: 'chat' })}
35
- >
36
- <ActionIcon
37
- icon={Eraser}
38
- placement={'bottom'}
39
- title={
40
- (<HotKeys desc={t('clearCurrentMessages', { ns: 'chat' })} keys={hotkeys} />) as any
41
- }
42
- />
43
- </Popconfirm>
44
- </>
45
- );
46
- });
47
-
48
- export default ActionsRight;
@@ -1,71 +0,0 @@
1
- import { ChatInputArea } from '@lobehub/ui';
2
- import { useResponsive } from 'antd-style';
3
- import dynamic from 'next/dynamic';
4
- import { ReactNode, Suspense, memo, useState } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
-
7
- import { CHAT_TEXTAREA_HEIGHT } from '@/const/layoutTokens';
8
- import { useSessionStore } from '@/store/session';
9
- import { agentSelectors } from '@/store/session/selectors';
10
-
11
- import ActionLeft from './ActionBar/ActionLeft';
12
- import ActionsRight from './ActionBar/ActionRight';
13
-
14
- const Token = dynamic(() => import('./ActionBar/Token'), { ssr: false });
15
-
16
- interface ChatContentProps {
17
- expand?: boolean;
18
- footer?: ReactNode;
19
- mobile?: boolean;
20
- onExpandChange?: (expand: boolean) => void;
21
- }
22
-
23
- const ChatInputContent = memo<ChatContentProps>(
24
- ({ expand, onExpandChange, mobile: defaultMobile, footer }) => {
25
- const { t } = useTranslation('common');
26
-
27
- const [message, setMessage] = useState('');
28
- const { mobile: runtimeMobile } = useResponsive();
29
-
30
- const mobile = runtimeMobile || defaultMobile;
31
-
32
- const [isLoading, sendMessage, stopGenerateMessage, showTokenTag] = useSessionStore((s) => [
33
- !!s.chatLoadingId,
34
- s.sendMessage,
35
- s.stopGenerateMessage,
36
- agentSelectors.showTokenTag(s),
37
- ]);
38
-
39
- return (
40
- <ChatInputArea
41
- actions={
42
- <>
43
- <ActionLeft />
44
- {showTokenTag && (
45
- <Suspense>
46
- <Token input={message} />
47
- </Suspense>
48
- )}
49
- </>
50
- }
51
- actionsRight={<ActionsRight />}
52
- expand={expand}
53
- footer={footer}
54
- loading={isLoading}
55
- minHeight={mobile ? 0 : CHAT_TEXTAREA_HEIGHT}
56
- onExpandChange={onExpandChange}
57
- onInputChange={setMessage}
58
- onSend={sendMessage}
59
- onStop={stopGenerateMessage}
60
- placeholder={t('sendPlaceholder', { ns: 'chat' })}
61
- text={{
62
- send: t('send'),
63
- stop: t('stop'),
64
- }}
65
- value={message}
66
- />
67
- );
68
- },
69
- );
70
-
71
- export default ChatInputContent;