@lobehub/chat 0.106.0 → 0.107.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 (214) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/locales/en_US/common.json +19 -0
  3. package/locales/en_US/migration.json +45 -0
  4. package/locales/en_US/setting.json +7 -7
  5. package/locales/ja_JP/common.json +19 -0
  6. package/locales/ja_JP/migration.json +45 -0
  7. package/locales/ja_JP/setting.json +2 -2
  8. package/locales/ko_KR/common.json +19 -0
  9. package/locales/ko_KR/migration.json +45 -0
  10. package/locales/ko_KR/setting.json +2 -2
  11. package/locales/ru_RU/common.json +19 -0
  12. package/locales/ru_RU/migration.json +45 -0
  13. package/locales/ru_RU/setting.json +2 -2
  14. package/locales/zh_CN/common.json +19 -0
  15. package/locales/zh_CN/migration.json +45 -0
  16. package/locales/zh_CN/setting.json +1 -1
  17. package/locales/zh_TW/common.json +19 -0
  18. package/locales/zh_TW/migration.json +45 -0
  19. package/locales/zh_TW/setting.json +2 -2
  20. package/package.json +2 -1
  21. package/src/app/api/openai/chat/createChatCompletion.test.ts +4 -4
  22. package/src/app/api/openai/chat/createChatCompletion.ts +4 -10
  23. package/src/app/api/openai/createBizOpenAI/index.ts +1 -1
  24. package/src/app/api/openai/errorResponse.test.ts +1 -1
  25. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +2 -1
  26. package/src/app/chat/(mobile)/features/ChatInput/SendButton.tsx +2 -2
  27. package/src/app/chat/features/ChatHeader/ShareButton/Inner.tsx +2 -5
  28. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +2 -2
  29. package/src/app/chat/features/ChatInput/ActionBar/Clear.tsx +2 -2
  30. package/src/app/chat/features/ChatInput/ActionBar/Token/TokenTag.tsx +8 -2
  31. package/src/app/chat/features/ChatInput/InputAreaInner/index.tsx +2 -2
  32. package/src/app/chat/features/ChatInput/STT/index.tsx +2 -2
  33. package/src/app/chat/features/ChatInput/Topic/index.tsx +2 -2
  34. package/src/app/chat/features/ChatInput/useSend.ts +3 -3
  35. package/src/app/chat/features/Conversation/ChatList/Actions/index.ts +2 -2
  36. package/src/app/chat/features/Conversation/ChatList/Error/ApiKeyForm.tsx +2 -2
  37. package/src/app/chat/features/Conversation/ChatList/Error/InvalidAccess.tsx +2 -2
  38. package/src/app/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +2 -2
  39. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.test.tsx +4 -4
  40. package/src/app/chat/features/Conversation/ChatList/Extras/Assistant.tsx +30 -31
  41. package/src/app/chat/features/Conversation/ChatList/Extras/TTS.tsx +2 -2
  42. package/src/app/chat/features/Conversation/ChatList/Extras/Translate.tsx +2 -2
  43. package/src/app/chat/features/Conversation/ChatList/Extras/User.tsx +8 -5
  44. package/src/app/chat/features/Conversation/ChatList/Extras/index.ts +3 -3
  45. package/src/app/chat/features/Conversation/ChatList/Extras/type.ts +5 -0
  46. package/src/app/chat/features/Conversation/ChatList/Messages/Assistant.tsx +10 -6
  47. package/src/app/chat/features/Conversation/ChatList/Messages/Default.tsx +7 -3
  48. package/src/app/chat/features/Conversation/ChatList/Messages/Function.tsx +7 -6
  49. package/src/app/chat/features/Conversation/ChatList/Messages/index.ts +7 -7
  50. package/src/app/chat/features/Conversation/ChatList/Plugins/Render/StandaloneType/Iframe.tsx +6 -6
  51. package/src/app/chat/features/Conversation/ChatList/index.tsx +16 -24
  52. package/src/app/chat/features/Conversation/ScrollAnchor/index.tsx +2 -2
  53. package/src/app/chat/features/Conversation/index.tsx +3 -3
  54. package/src/app/chat/features/Conversation/useInitConversation.ts +43 -0
  55. package/src/app/chat/features/Migration/ExportConfigButton.tsx +33 -0
  56. package/src/app/chat/features/Migration/Failed.tsx +115 -0
  57. package/src/app/chat/features/Migration/Modal.tsx +80 -0
  58. package/src/app/chat/features/Migration/Start.tsx +106 -0
  59. package/src/app/chat/features/Migration/UpgradeButton.tsx +74 -0
  60. package/src/app/chat/features/Migration/const.ts +15 -0
  61. package/src/app/chat/features/Migration/index.tsx +44 -0
  62. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +54 -0
  63. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +5 -4
  64. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +22 -28
  65. package/src/app/chat/features/SessionListContent/SearchMode.tsx +15 -0
  66. package/src/app/chat/features/SessionListContent/index.tsx +4 -44
  67. package/src/app/chat/features/SessionSearchBar/index.tsx +11 -3
  68. package/src/app/chat/features/TopicListContent/Header.tsx +4 -4
  69. package/src/app/chat/features/TopicListContent/Topic/TopicContent.tsx +11 -9
  70. package/src/app/chat/features/TopicListContent/Topic/TopicItem.tsx +15 -13
  71. package/src/app/chat/features/TopicListContent/Topic/index.tsx +9 -16
  72. package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +13 -4
  73. package/src/app/chat/features/TopicListContent/index.tsx +1 -2
  74. package/src/app/chat/page.tsx +6 -1
  75. package/src/app/chat/settings/features/HeaderContent.tsx +3 -3
  76. package/src/app/home/Redirect.tsx +13 -7
  77. package/src/app/settings/(mobile)/features/ExtraList.tsx +12 -4
  78. package/src/app/settings/common/Common.tsx +16 -4
  79. package/src/app/welcome/features/Banner/index.tsx +9 -10
  80. package/src/chains/summaryTitle.ts +5 -2
  81. package/src/components/DataStyleModal/index.tsx +64 -0
  82. package/src/config/client.ts +5 -0
  83. package/src/const/session.ts +21 -0
  84. package/src/const/settings.ts +1 -1
  85. package/src/database/core/__tests__/db.test.ts +8 -5
  86. package/src/database/core/__tests__/model.test.ts +3 -3
  87. package/src/database/core/db.ts +38 -10
  88. package/src/database/core/index.ts +1 -0
  89. package/src/database/core/model.ts +121 -10
  90. package/src/database/core/schemas.ts +22 -0
  91. package/src/database/core/types/db.ts +13 -0
  92. package/src/database/models/__DEBUG.ts +119 -0
  93. package/src/database/models/{file.test.ts → __tests__/file.test.ts} +19 -6
  94. package/src/database/models/__tests__/message.test.ts +314 -0
  95. package/src/database/models/__tests__/session.test.ts +117 -0
  96. package/src/database/models/__tests__/topic.test.ts +250 -0
  97. package/src/database/models/file.ts +9 -5
  98. package/src/database/models/message.ts +186 -0
  99. package/src/database/models/session.ts +160 -0
  100. package/src/database/models/topic.ts +196 -0
  101. package/src/{types/database → database/schemas}/files.ts +2 -2
  102. package/src/database/schemas/message.ts +37 -0
  103. package/src/database/schemas/session.ts +56 -0
  104. package/src/database/schemas/topic.ts +12 -0
  105. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  106. package/src/features/AgentSetting/store/action.ts +1 -1
  107. package/src/features/AgentSetting/store/initialState.ts +1 -1
  108. package/src/features/AgentSetting/store/reducers/config.ts +1 -1
  109. package/src/features/DataImporter/index.tsx +240 -0
  110. package/src/features/DebugUI/index.tsx +34 -0
  111. package/src/features/SideBar/BottomActions.tsx +8 -14
  112. package/src/hooks/useImportConfig.ts +13 -30
  113. package/src/hooks/useTTS.ts +1 -1
  114. package/src/layout/GlobalLayout/index.tsx +16 -1
  115. package/src/locales/default/common.ts +19 -0
  116. package/src/locales/default/migration.ts +47 -0
  117. package/src/locales/default/setting.ts +1 -1
  118. package/src/locales/resources/en_US.ts +2 -0
  119. package/src/locales/resources/ja_JP.ts +2 -0
  120. package/src/locales/resources/ko_KR.ts +2 -0
  121. package/src/locales/resources/ru_RU.ts +2 -0
  122. package/src/locales/resources/zh_CN.ts +2 -0
  123. package/src/locales/resources/zh_TW.ts +2 -0
  124. package/src/migrations/FromV0ToV1.ts +2 -4
  125. package/src/migrations/FromV1ToV2/fixtures/input-v1-session.json +191 -0
  126. package/src/migrations/FromV1ToV2/fixtures/output-v2.json +202 -0
  127. package/src/migrations/FromV1ToV2/index.ts +82 -0
  128. package/src/migrations/FromV1ToV2/migrations.test.ts +224 -0
  129. package/src/migrations/FromV1ToV2/types/v1.ts +78 -0
  130. package/src/migrations/FromV1ToV2/types/v2.ts +52 -0
  131. package/src/{utils → migrations}/VersionController.test.ts +1 -3
  132. package/src/{utils → migrations}/VersionController.ts +5 -2
  133. package/src/migrations/index.ts +17 -3
  134. package/src/services/__tests__/file.test.ts +3 -3
  135. package/src/services/chat.ts +66 -5
  136. package/src/services/config.ts +184 -0
  137. package/src/services/debug.ts +13 -0
  138. package/src/services/file.ts +6 -2
  139. package/src/services/github.ts +25 -0
  140. package/src/services/message.ts +79 -0
  141. package/src/services/session.ts +63 -0
  142. package/src/services/topic.ts +56 -0
  143. package/src/store/chat/actions/enhance.ts +95 -0
  144. package/src/store/{session/slices/chat → chat}/actions/message.ts +225 -247
  145. package/src/store/{session/slices/chat → chat}/actions/plugin.ts +29 -34
  146. package/src/store/{session/slices/chat → chat}/actions/share.ts +9 -13
  147. package/src/store/chat/actions/topic.ts +197 -0
  148. package/src/store/chat/helpers.ts +23 -0
  149. package/src/store/chat/index.ts +3 -0
  150. package/src/store/chat/initialState.ts +48 -0
  151. package/src/store/chat/reducers/message.test.ts +83 -0
  152. package/src/store/chat/reducers/message.ts +83 -0
  153. package/src/store/chat/selectors/__tests__/topic.test.ts +43 -0
  154. package/src/store/chat/selectors/chat.ts +119 -0
  155. package/src/store/chat/selectors/index.ts +2 -0
  156. package/src/store/chat/selectors/topic.ts +28 -0
  157. package/src/store/chat/store.ts +63 -0
  158. package/src/store/files/slices/images/action.ts +6 -4
  159. package/src/store/global/store.ts +1 -1
  160. package/src/store/middleware/createHyperStorage/index.ts +40 -24
  161. package/src/store/middleware/createHyperStorage/keyMapper.ts +8 -2
  162. package/src/store/middleware/createHyperStorage/type.ts +10 -8
  163. package/src/store/plugin/slices/plugin/action.ts +1 -1
  164. package/src/store/plugin/store.ts +2 -4
  165. package/src/store/session/helpers.ts +1 -1
  166. package/src/store/session/hooks/useSessionHydrated.ts +6 -10
  167. package/src/store/session/initialState.ts +26 -7
  168. package/src/store/session/selectors.ts +1 -2
  169. package/src/store/session/slices/agent/action.ts +26 -35
  170. package/src/store/session/slices/agent/selectors.ts +32 -37
  171. package/src/store/session/slices/session/action.ts +81 -88
  172. package/src/store/session/slices/session/helpers.ts +19 -0
  173. package/src/store/session/slices/session/initialState.ts +7 -10
  174. package/src/store/session/slices/session/selectors/export.ts +12 -9
  175. package/src/store/session/slices/session/selectors/index.ts +2 -35
  176. package/src/store/session/slices/session/selectors/list.test.ts +25 -203
  177. package/src/store/session/slices/session/selectors/list.ts +40 -78
  178. package/src/store/session/store.ts +11 -17
  179. package/src/types/agent/index.ts +61 -0
  180. package/src/types/chatMessage.ts +11 -20
  181. package/src/types/exportConfig.ts +7 -2
  182. package/src/types/files.ts +2 -2
  183. package/src/types/llm.ts +1 -1
  184. package/src/types/meta.ts +24 -12
  185. package/src/types/openai/chat.ts +9 -1
  186. package/src/types/openai/functionCall.ts +4 -0
  187. package/src/types/session.ts +10 -94
  188. package/src/types/translate.ts +2 -1
  189. package/src/utils/config.ts +5 -11
  190. package/src/utils/fetch.ts +8 -2
  191. package/src/app/chat/features/Conversation/usePluginsInit.ts +0 -14
  192. package/src/database/core/schema.ts +0 -9
  193. package/src/helpers/export.ts +0 -69
  194. package/src/hooks/useExportConfig.ts +0 -6
  195. package/src/store/session/slices/chat/actions/index.ts +0 -32
  196. package/src/store/session/slices/chat/actions/topic.ts +0 -171
  197. package/src/store/session/slices/chat/actions/translate.ts +0 -113
  198. package/src/store/session/slices/chat/helpers.ts +0 -9
  199. package/src/store/session/slices/chat/initialState.ts +0 -13
  200. package/src/store/session/slices/chat/reducers/message.test.ts +0 -319
  201. package/src/store/session/slices/chat/reducers/message.ts +0 -156
  202. package/src/store/session/slices/chat/reducers/topic.test.ts +0 -179
  203. package/src/store/session/slices/chat/reducers/topic.ts +0 -81
  204. package/src/store/session/slices/chat/selectors/chat.ts +0 -111
  205. package/src/store/session/slices/chat/selectors/index.ts +0 -26
  206. package/src/store/session/slices/chat/selectors/topic.ts +0 -30
  207. package/src/store/session/slices/chat/selectors/utils.test.ts +0 -385
  208. package/src/store/session/slices/chat/selectors/utils.ts +0 -91
  209. package/src/store/session/slices/chat/utils.ts +0 -13
  210. package/src/store/session/slices/session/reducers/session.test.ts +0 -577
  211. package/src/store/session/slices/session/reducers/session.ts +0 -182
  212. package/src/types/database/db.ts +0 -4
  213. /package/src/store/{session/slices/chat → chat}/reducers/files.test.ts +0 -0
  214. /package/src/store/{session/slices/chat → chat}/reducers/files.ts +0 -0
@@ -0,0 +1,54 @@
1
+ import { CollapseProps } from 'antd';
2
+ import isEqual from 'fast-deep-equal';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { preferenceSelectors, useGlobalStore } from '@/store/global';
7
+ import { useSessionStore } from '@/store/session';
8
+ import { sessionSelectors } from '@/store/session/selectors';
9
+
10
+ import CollapseGroup from './CollapseGroup';
11
+ import Inbox from './Inbox';
12
+ import SessionList from './List';
13
+
14
+ const SessionListContent = memo(() => {
15
+ const { t } = useTranslation('chat');
16
+ const unpinnedSessionList = useSessionStore(sessionSelectors.unpinnedSessionList, isEqual);
17
+ const pinnedList = useSessionStore(sessionSelectors.pinnedSessionList, isEqual);
18
+ const hasPinnedSessionList = useSessionStore(sessionSelectors.hasPinnedSessionList);
19
+
20
+ const [sessionGroupKeys, updatePreference] = useGlobalStore((s) => [
21
+ preferenceSelectors.sessionGroupKeys(s),
22
+ s.updatePreference,
23
+ ]);
24
+
25
+ const items = [
26
+ hasPinnedSessionList && {
27
+ children: <SessionList dataSource={pinnedList} />,
28
+ key: 'pinned',
29
+ label: t('pin'),
30
+ },
31
+ {
32
+ children: <SessionList dataSource={unpinnedSessionList} />,
33
+ key: 'sessionList',
34
+ label: t('sessionList'),
35
+ },
36
+ ].filter(Boolean) as CollapseProps['items'];
37
+
38
+ return (
39
+ <>
40
+ <Inbox />
41
+ <CollapseGroup
42
+ activeKey={sessionGroupKeys}
43
+ items={items}
44
+ onChange={(keys) => {
45
+ const sessionGroupKeys = typeof keys === 'string' ? [keys] : keys;
46
+
47
+ updatePreference({ sessionGroupKeys });
48
+ }}
49
+ />
50
+ </>
51
+ );
52
+ });
53
+
54
+ export default SessionListContent;
@@ -5,8 +5,9 @@ import { HardDriveDownload, MoreVertical, Pin, PinOff, Trash } from 'lucide-reac
5
5
  import { memo, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
- import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
8
+ import { configService } from '@/services/config';
9
9
  import { useSessionStore } from '@/store/session';
10
+ import { sessionHelpers } from '@/store/session/helpers';
10
11
  import { sessionSelectors } from '@/store/session/selectors';
11
12
 
12
13
  const useStyles = createStyles(({ css }) => ({
@@ -27,7 +28,7 @@ const Actions = memo<ActionProps>(({ id, setOpen }) => {
27
28
 
28
29
  const [pin, removeSession, pinSession] = useSessionStore((s) => {
29
30
  const session = sessionSelectors.getSessionById(id)(s);
30
- return [session.pinned, s.removeSession, s.pinSession];
31
+ return [sessionHelpers.getSessionPinned(session), s.removeSession, s.pinSession];
31
32
  });
32
33
 
33
34
  const { modal } = App.useApp();
@@ -48,14 +49,14 @@ const Actions = memo<ActionProps>(({ id, setOpen }) => {
48
49
  key: 'agent',
49
50
  label: <div>{t('exportType.agent')}</div>,
50
51
  onClick: () => {
51
- exportSingleAgent(id);
52
+ configService.exportSingleAgent(id);
52
53
  },
53
54
  },
54
55
  {
55
56
  key: 'agentWithMessage',
56
57
  label: <div>{t('exportType.agentWithMessage')}</div>,
57
58
  onClick: () => {
58
- exportSingleSession(id);
59
+ configService.exportSingleSession(id);
59
60
  },
60
61
  },
61
62
  ],
@@ -4,8 +4,10 @@ import { memo, useMemo, useState } from 'react';
4
4
  import { Flexbox } from 'react-layout-kit';
5
5
  import { shallow } from 'zustand/shallow';
6
6
 
7
+ import { useChatStore } from '@/store/chat';
7
8
  import { settingsSelectors, useGlobalStore } from '@/store/global';
8
9
  import { useSessionStore } from '@/store/session';
10
+ import { sessionHelpers } from '@/store/session/helpers';
9
11
  import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
10
12
 
11
13
  import ListItem from '../../ListItem';
@@ -19,36 +21,28 @@ const SessionItem = memo<SessionItemProps>(({ id }) => {
19
21
  const [open, setOpen] = useState(false);
20
22
 
21
23
  const [defaultModel] = useGlobalStore((s) => [settingsSelectors.defaultAgentConfig(s).model]);
24
+ const [active, loading] = useChatStore((s) => [
25
+ s.activeId === id,
26
+ !!s.chatLoadingId && id === s.activeId,
27
+ ]);
22
28
 
23
- const [
24
- active,
25
- loading,
26
- pin,
27
- title,
28
- description,
29
- systemRole,
30
- avatar,
31
- avatarBackground,
32
- updateAt,
33
- model,
34
- ] = useSessionStore((s) => {
35
- const session = sessionSelectors.getSessionById(id)(s);
36
- const meta = session.meta;
37
- const systemRole = session.config.systemRole;
29
+ const [pin, title, description, systemRole, avatar, avatarBackground, updateAt, model] =
30
+ useSessionStore((s) => {
31
+ const session = sessionSelectors.getSessionById(id)(s);
32
+ const meta = session.meta;
33
+ const systemRole = session.config.systemRole;
38
34
 
39
- return [
40
- s.activeId === id,
41
- !!s.chatLoadingId && id === s.activeId,
42
- session.pinned,
43
- agentSelectors.getTitle(meta),
44
- agentSelectors.getDescription(meta),
45
- systemRole,
46
- agentSelectors.getAvatar(meta),
47
- meta.backgroundColor,
48
- session?.updateAt,
49
- session.config.model,
50
- ];
51
- });
35
+ return [
36
+ sessionHelpers.getSessionPinned(session),
37
+ agentSelectors.getTitle(meta),
38
+ agentSelectors.getDescription(meta),
39
+ systemRole,
40
+ agentSelectors.getAvatar(meta),
41
+ meta.backgroundColor,
42
+ session?.updatedAt,
43
+ session.config.model,
44
+ ];
45
+ });
52
46
 
53
47
  const showModel = model !== defaultModel;
54
48
 
@@ -0,0 +1,15 @@
1
+ import isEqual from 'fast-deep-equal';
2
+ import { memo } from 'react';
3
+
4
+ import { useSessionStore } from '@/store/session';
5
+ import { sessionSelectors } from '@/store/session/selectors';
6
+
7
+ import SessionList from './List';
8
+
9
+ const SessionListContent = memo(() => {
10
+ const searchSessions = useSessionStore(sessionSelectors.searchSessions, isEqual);
11
+
12
+ return <SessionList dataSource={searchSessions} />;
13
+ });
14
+
15
+ export default SessionListContent;
@@ -1,54 +1,14 @@
1
- import { CollapseProps } from 'antd';
2
- import isEqual from 'fast-deep-equal';
3
1
  import { memo } from 'react';
4
- import { useTranslation } from 'react-i18next';
5
2
 
6
- import { preferenceSelectors, useGlobalStore } from '@/store/global';
7
3
  import { useSessionStore } from '@/store/session';
8
- import { sessionSelectors } from '@/store/session/selectors';
9
4
 
10
- import CollapseGroup from './CollapseGroup';
11
- import Inbox from './Inbox';
12
- import SessionList from './List';
5
+ import DefaultMode from './DefaultMode';
6
+ import SearchMode from './SearchMode';
13
7
 
14
8
  const SessionListContent = memo(() => {
15
- const { t } = useTranslation('chat');
16
- const unpinnedSessionList = useSessionStore(sessionSelectors.unpinnedSessionList, isEqual);
17
- const pinnedList = useSessionStore(sessionSelectors.pinnedSessionList, isEqual);
18
- const hasPinnedSessionList = useSessionStore(sessionSelectors.hasPinnedSessionList);
9
+ const isSearching = useSessionStore((s) => s.isSearching);
19
10
 
20
- const [sessionGroupKeys, updatePreference] = useGlobalStore((s) => [
21
- preferenceSelectors.sessionGroupKeys(s),
22
- s.updatePreference,
23
- ]);
24
-
25
- const items = [
26
- hasPinnedSessionList && {
27
- children: <SessionList dataSource={pinnedList} />,
28
- key: 'pinned',
29
- label: t('pin'),
30
- },
31
- {
32
- children: <SessionList dataSource={unpinnedSessionList} />,
33
- key: 'sessionList',
34
- label: t('sessionList'),
35
- },
36
- ].filter(Boolean) as CollapseProps['items'];
37
-
38
- return (
39
- <>
40
- <Inbox />
41
- <CollapseGroup
42
- activeKey={sessionGroupKeys}
43
- items={items}
44
- onChange={(keys) => {
45
- const sessionGroupKeys = typeof keys === 'string' ? [keys] : keys;
46
-
47
- updatePreference({ sessionGroupKeys });
48
- }}
49
- />
50
- </>
51
- );
11
+ return isSearching ? <SearchMode /> : <DefaultMode />;
52
12
  });
53
13
 
54
14
  export default SessionListContent;
@@ -1,19 +1,27 @@
1
1
  import { SearchBar } from '@lobehub/ui';
2
2
  import { useResponsive } from 'antd-style';
3
- import { memo } from 'react';
3
+ import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
6
  import { useSessionStore } from '@/store/session';
7
7
 
8
8
  const SessionSearchBar = memo(() => {
9
9
  const { t } = useTranslation('chat');
10
- const [keywords] = useSessionStore((s) => [s.searchKeywords]);
10
+ const [keywords, setKeywords] = useState<string | undefined>(undefined);
11
+ const [useSearchSessions] = useSessionStore((s) => [s.useSearchSessions]);
12
+
13
+ useSearchSessions(keywords);
14
+
11
15
  const { mobile } = useResponsive();
12
16
  return (
13
17
  <SearchBar
14
18
  allowClear
15
19
  enableShortKey={!mobile}
16
- onChange={(e) => useSessionStore.setState({ searchKeywords: e.target.value })}
20
+ onChange={(e) => {
21
+ const newKeywords = e.target.value;
22
+ setKeywords(newKeywords);
23
+ useSessionStore.setState({ isSearching: !!newKeywords });
24
+ }}
17
25
  placeholder={t('searchAgentPlaceholder')}
18
26
  shortKey={'k'}
19
27
  spotlight={!mobile}
@@ -5,18 +5,18 @@ import { memo, useMemo, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Flexbox } from 'react-layout-kit';
7
7
 
8
- import { useSessionStore } from '@/store/session';
9
- import { topicSelectors } from '@/store/session/selectors';
8
+ import { useChatStore } from '@/store/chat';
9
+ import { topicSelectors } from '@/store/chat/selectors';
10
10
 
11
11
  import SidebarHeader from '../SidebarHeader';
12
12
  import TopicSearchBar from './TopicSearchBar';
13
13
 
14
14
  const Header = memo(() => {
15
15
  const { t } = useTranslation('chat');
16
- const [topicLength, removeUnstarredTopic, removeAllTopic] = useSessionStore((s) => [
16
+ const [topicLength, removeUnstarredTopic, removeAllTopic] = useChatStore((s) => [
17
17
  topicSelectors.currentTopicLength(s),
18
18
  s.removeUnstarredTopic,
19
- s.removeAllTopic,
19
+ s.removeSessionTopics,
20
20
  ]);
21
21
 
22
22
  const [showSearch, setShowSearch] = useState(false);
@@ -6,7 +6,7 @@ import { memo, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
- import { useSessionStore } from '@/store/session';
9
+ import { useChatStore } from '@/store/chat';
10
10
 
11
11
  const useStyles = createStyles(({ css }) => ({
12
12
  content: css`
@@ -26,21 +26,23 @@ const { Paragraph } = Typography;
26
26
  interface TopicContentProps {
27
27
  fav?: boolean;
28
28
  id: string;
29
+ showMore?: boolean;
29
30
  title: string;
30
31
  }
31
32
 
32
- const TopicContent = memo<TopicContentProps>(({ id, title, fav }) => {
33
+ const TopicContent = memo<TopicContentProps>(({ id, title, fav, showMore }) => {
33
34
  const { t } = useTranslation('common');
34
35
 
35
- const [editing, dispatchTopic, removeTopic] = useSessionStore((s) => [
36
- s.renameTopicId === id,
37
- s.dispatchTopic,
36
+ const [editing, favoriteTopic, updateTopicTitle, removeTopic] = useChatStore((s) => [
37
+ s.topicRenamingId === id,
38
+ s.favoriteTopic,
39
+ s.updateTopicTitle,
38
40
  s.removeTopic,
39
41
  ]);
40
42
  const { styles, theme } = useStyles();
41
43
 
42
44
  const toggleEditing = (visible?: boolean) => {
43
- useSessionStore.setState({ renameTopicId: visible ? id : '' });
45
+ useChatStore.setState({ topicRenamingId: visible ? id : '' });
44
46
  };
45
47
 
46
48
  const { modal } = App.useApp();
@@ -99,7 +101,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav }) => {
99
101
  icon={Star}
100
102
  onClick={() => {
101
103
  if (!id) return;
102
- dispatchTopic({ favorite: !fav, id, type: 'favorChatTopic' });
104
+ favoriteTopic(id, !fav);
103
105
  }}
104
106
  size={'small'}
105
107
  />
@@ -116,7 +118,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav }) => {
116
118
  editing={editing}
117
119
  onChangeEnd={(v) => {
118
120
  if (title !== v) {
119
- dispatchTopic({ id, key: 'title', type: 'updateChatTopic', value: v });
121
+ updateTopicTitle(id, v);
120
122
  }
121
123
  toggleEditing(false);
122
124
  }}
@@ -130,7 +132,7 @@ const TopicContent = memo<TopicContentProps>(({ id, title, fav }) => {
130
132
  value={title}
131
133
  />
132
134
  )}
133
- {!editing && (
135
+ {showMore && !editing && (
134
136
  <Dropdown
135
137
  arrow={false}
136
138
  menu={{
@@ -1,8 +1,8 @@
1
1
  import { createStyles } from 'antd-style';
2
- import { memo } from 'react';
2
+ import { memo, useState } from 'react';
3
3
  import { Flexbox } from 'react-layout-kit';
4
4
 
5
- import { useSessionStore } from '@/store/session';
5
+ import { useChatStore } from '@/store/chat';
6
6
 
7
7
  import DefaultContent from './DefaultContent';
8
8
  import TopicContent from './TopicContent';
@@ -21,17 +21,8 @@ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
21
21
  padding: 8px;
22
22
  border-radius: ${token.borderRadius}px;
23
23
 
24
- .topic-more {
25
- opacity: 0;
26
- transition: opacity 400ms ${token.motionEaseOut};
27
- }
28
-
29
24
  &:hover {
30
25
  background: ${token.colorFillSecondary};
31
-
32
- .topic-more {
33
- opacity: 1;
34
- }
35
26
  }
36
27
  `,
37
28
  split: css`
@@ -49,7 +40,8 @@ export interface ConfigCellProps {
49
40
  const TopicItem = memo<ConfigCellProps>(({ title, active, id, fav }) => {
50
41
  const { styles, cx } = useStyles();
51
42
 
52
- const [toggleTopic] = useSessionStore((s) => [s.toggleTopic]);
43
+ const [toggleTopic] = useChatStore((s) => [s.switchTopic]);
44
+ const [isHover, setHovering] = useState(false);
53
45
 
54
46
  return (
55
47
  <Flexbox
@@ -60,8 +52,18 @@ const TopicItem = memo<ConfigCellProps>(({ title, active, id, fav }) => {
60
52
  onClick={() => {
61
53
  toggleTopic(id);
62
54
  }}
55
+ onMouseEnter={() => {
56
+ setHovering(true);
57
+ }}
58
+ onMouseLeave={() => {
59
+ setHovering(false);
60
+ }}
63
61
  >
64
- {!id ? <DefaultContent /> : <TopicContent fav={fav} id={id} title={title} />}
62
+ {!id ? (
63
+ <DefaultContent />
64
+ ) : (
65
+ <TopicContent fav={fav} id={id} showMore={isHover} title={title} />
66
+ )}
65
67
  </Flexbox>
66
68
  );
67
69
  });
@@ -1,37 +1,30 @@
1
1
  import { EmptyCard } from '@lobehub/ui';
2
2
  import { useThemeMode } from 'antd-style';
3
3
  import isEqual from 'fast-deep-equal';
4
- import { useMemo } from 'react';
5
4
  import { useTranslation } from 'react-i18next';
6
5
  import { Flexbox } from 'react-layout-kit';
7
6
 
7
+ import { useChatStore } from '@/store/chat';
8
+ import { topicSelectors } from '@/store/chat/selectors';
8
9
  import { useGlobalStore } from '@/store/global';
9
- import { useSessionChatInit, useSessionStore } from '@/store/session';
10
- import { topicSelectors } from '@/store/session/selectors';
11
10
 
12
11
  import SkeletonList from './SkeletonList';
13
12
  import TopicItem from './TopicItem';
14
13
 
15
14
  export const Topic = () => {
16
- const init = useSessionChatInit();
17
- const topics = useSessionStore(topicSelectors.currentTopics, isEqual);
18
15
  const { isDarkMode } = useThemeMode();
19
- const [activeTopicId, keywords] = useSessionStore((s) => [
20
- s.activeTopicId,
21
- s.topicSearchKeywords,
22
- ]);
16
+
17
+ const topics = useChatStore(topicSelectors.displayTopics, isEqual);
18
+
19
+ const [topicsInit, activeTopicId] = useChatStore((s) => [s.topicsInit, s.activeTopicId]);
20
+
23
21
  const { t } = useTranslation('empty');
24
22
  const [visible, updateGuideState] = useGlobalStore((s) => [
25
23
  s.preference.guide?.topic,
26
24
  s.updateGuideState,
27
25
  ]);
28
26
 
29
- const topicsData = useMemo(() => {
30
- if (!keywords) return topics;
31
- return topics.filter(({ title }) => title.toLowerCase().includes(keywords.toLowerCase()));
32
- }, [topics, keywords]);
33
-
34
- return !init ? (
27
+ return !topicsInit ? (
35
28
  <SkeletonList />
36
29
  ) : (
37
30
  <Flexbox gap={2} style={{ marginBottom: 12 }}>
@@ -57,7 +50,7 @@ export const Topic = () => {
57
50
  title={t('topic.defaultTitle', { ns: 'chat' })}
58
51
  />
59
52
 
60
- {topicsData.map(({ id, favorite, title }) => (
53
+ {topics.map(({ id, favorite, title }) => (
61
54
  <TopicItem active={activeTopicId === id} fav={favorite} id={id} key={id} title={title} />
62
55
  ))}
63
56
  </Flexbox>
@@ -1,14 +1,20 @@
1
1
  import { SearchBar } from '@lobehub/ui';
2
2
  import { useResponsive } from 'antd-style';
3
- import { memo } from 'react';
3
+ import { memo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
- import { useSessionStore } from '@/store/session';
6
+ import { useChatStore } from '@/store/chat';
7
7
 
8
8
  const TopicSearchBar = memo<{ onClear?: () => void }>(({ onClear }) => {
9
9
  const { t } = useTranslation('chat');
10
- const [keywords] = useSessionStore((s) => [s.topicSearchKeywords]);
10
+
11
+ const [keywords, setKeywords] = useState<string | undefined>(undefined);
11
12
  const { mobile } = useResponsive();
13
+
14
+ const useSearchTopics = useChatStore((s) => s.useSearchTopics);
15
+
16
+ useSearchTopics(keywords);
17
+
12
18
  return (
13
19
  <SearchBar
14
20
  autoFocus
@@ -16,7 +22,10 @@ const TopicSearchBar = memo<{ onClear?: () => void }>(({ onClear }) => {
16
22
  if (keywords === '') onClear?.();
17
23
  }}
18
24
  onChange={(e) => {
19
- useSessionStore.setState({ topicSearchKeywords: e.target.value });
25
+ const value = e.target.value;
26
+
27
+ setKeywords(value);
28
+ useChatStore.setState({ isSearchingTopic: !!value });
20
29
  }}
21
30
  placeholder={t('topic.searchPlaceholder')}
22
31
  spotlight={!mobile}
@@ -1,10 +1,9 @@
1
1
  import { memo } from 'react';
2
2
  import { Flexbox } from 'react-layout-kit';
3
3
 
4
- import TopicSearchBar from '@/app/chat/features/TopicListContent/TopicSearchBar';
5
-
6
4
  import Header from './Header';
7
5
  import { Topic } from './Topic';
6
+ import TopicSearchBar from './TopicSearchBar';
8
7
 
9
8
  const TopicListContent = memo<{ mobile?: boolean }>(({ mobile }) => {
10
9
  return (
@@ -2,13 +2,18 @@ import { isMobileDevice } from '@/utils/responsive';
2
2
 
3
3
  import DesktopPage from './(desktop)';
4
4
  import MobilePage from './(mobile)';
5
+ import Migration from './features/Migration';
5
6
 
6
7
  const Page = () => {
7
8
  const mobile = isMobileDevice();
8
9
 
9
10
  const Page = mobile ? MobilePage : DesktopPage;
10
11
 
11
- return <Page />;
12
+ return (
13
+ <Migration>
14
+ <Page />
15
+ </Migration>
16
+ );
12
17
  };
13
18
 
14
19
  export default Page;
@@ -6,7 +6,7 @@ import { memo, useMemo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
8
8
  import { HEADER_ICON_SIZE } from '@/const/layoutTokens';
9
- import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
9
+ import { configService } from '@/services/config';
10
10
  import { useSessionStore } from '@/store/session';
11
11
 
12
12
  import SubmitAgentButton from './SubmitAgentButton';
@@ -25,7 +25,7 @@ export const HeaderContent = memo<{ mobile?: boolean }>(() => {
25
25
  onClick: () => {
26
26
  if (!id) return;
27
27
 
28
- exportSingleAgent(id);
28
+ configService.exportSingleAgent(id);
29
29
  },
30
30
  },
31
31
  {
@@ -34,7 +34,7 @@ export const HeaderContent = memo<{ mobile?: boolean }>(() => {
34
34
  onClick: () => {
35
35
  if (!id) return;
36
36
 
37
- exportSingleSession(id);
37
+ configService.exportSingleSession(id);
38
38
  },
39
39
  },
40
40
  ],
@@ -3,20 +3,26 @@
3
3
  import { useRouter } from 'next/navigation';
4
4
  import { memo, useEffect } from 'react';
5
5
 
6
+ import { messageService } from '@/services/message';
7
+ import { sessionService } from '@/services/session';
6
8
  import { useSessionStore } from '@/store/session';
7
- import { sessionSelectors } from '@/store/session/selectors';
9
+
10
+ const checkHasConversation = async () => {
11
+ const hasMessages = await messageService.hasMessages();
12
+ const hasAgents = await sessionService.hasSessions();
13
+ return hasMessages || hasAgents;
14
+ };
8
15
 
9
16
  const Redirect = memo(() => {
10
17
  const router = useRouter();
18
+ const [switchSession] = useSessionStore((s) => [s.switchSession]);
11
19
 
12
20
  useEffect(() => {
13
- useSessionStore.persist.onFinishHydration(() => {
14
- const store = useSessionStore.getState();
15
- const hasConversion = sessionSelectors.hasConversion(store);
16
-
17
- if (hasConversion) {
21
+ checkHasConversation().then((hasData) => {
22
+ if (hasData) {
18
23
  router.push('/chat');
19
- store.switchSession();
24
+
25
+ switchSession();
20
26
  } else {
21
27
  router.push('/welcome');
22
28
  }
@@ -5,15 +5,15 @@ import { memo } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
7
  import { ABOUT, CHANGELOG, FEEDBACK } from '@/const/url';
8
- import { useExportConfig } from '@/hooks/useExportConfig';
9
8
  import { useImportConfig } from '@/hooks/useImportConfig';
9
+ import { configService } from '@/services/config';
10
10
  import { useGlobalStore } from '@/store/global';
11
11
 
12
12
  import Item from '../../features/SideBar/Item';
13
13
 
14
14
  const ExtraList = memo(() => {
15
15
  const { t } = useTranslation('common');
16
- const { exportAll } = useExportConfig();
16
+
17
17
  const { importConfig } = useImportConfig();
18
18
  const tab = useGlobalStore((s) => s.settingsTab);
19
19
  const { mobile } = useResponsive();
@@ -21,7 +21,7 @@ const ExtraList = memo(() => {
21
21
  {
22
22
  icon: HardDriveDownload,
23
23
  label: t('export'),
24
- onClick: exportAll,
24
+ onClick: configService.exportAll,
25
25
  value: 'export',
26
26
  },
27
27
  {
@@ -46,7 +46,15 @@ const ExtraList = memo(() => {
46
46
 
47
47
  return (
48
48
  <>
49
- <Upload maxCount={1} onChange={importConfig} showUploadList={false}>
49
+ <Upload
50
+ beforeUpload={(file) => {
51
+ importConfig(file);
52
+
53
+ return false;
54
+ }}
55
+ maxCount={1}
56
+ showUploadList={false}
57
+ >
50
58
  <Item icon={HardDriveUpload} label={t('import')} style={{ width: '100vw' }} />
51
59
  </Upload>
52
60
  {items.map(({ value, icon, label, onClick }) => (