@lobehub/chat 0.120.6 → 0.121.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 (121) hide show
  1. package/.github/ISSUE_TEMPLATE/2_feature_request_cn.yml +1 -1
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +9 -9
  4. package/README.zh-CN.md +9 -9
  5. package/docs/Development/Feature-Development-Frontend.zh-CN.md +126 -0
  6. package/docs/Development/Feature-Development.zh-CN.md +664 -83
  7. package/docs/Development/{State-Management/State-Management-Intro.zh-CN.md → State-Management-Intro.zh-CN.md} +9 -9
  8. package/docs/Home.md +1 -0
  9. package/docs/_Sidebar.md +1 -0
  10. package/locales/ar/chat.json +13 -1
  11. package/locales/ar/common.json +1 -0
  12. package/locales/de-DE/chat.json +13 -1
  13. package/locales/de-DE/common.json +1 -0
  14. package/locales/en-US/chat.json +13 -1
  15. package/locales/en-US/common.json +1 -0
  16. package/locales/es-ES/chat.json +13 -1
  17. package/locales/es-ES/common.json +1 -0
  18. package/locales/fr-FR/chat.json +13 -1
  19. package/locales/fr-FR/common.json +1 -0
  20. package/locales/it-IT/chat.json +13 -1
  21. package/locales/it-IT/common.json +1 -0
  22. package/locales/ja-JP/chat.json +13 -1
  23. package/locales/ja-JP/common.json +1 -0
  24. package/locales/ko-KR/chat.json +13 -1
  25. package/locales/ko-KR/common.json +1 -0
  26. package/locales/nl-NL/chat.json +13 -1
  27. package/locales/nl-NL/common.json +1 -0
  28. package/locales/pl-PL/chat.json +13 -1
  29. package/locales/pl-PL/common.json +1 -0
  30. package/locales/pt-BR/chat.json +13 -1
  31. package/locales/pt-BR/common.json +1 -0
  32. package/locales/ru-RU/chat.json +13 -1
  33. package/locales/ru-RU/common.json +1 -0
  34. package/locales/tr-TR/chat.json +13 -1
  35. package/locales/tr-TR/common.json +1 -0
  36. package/locales/vi-VN/chat.json +13 -1
  37. package/locales/vi-VN/common.json +1 -0
  38. package/locales/zh-CN/chat.json +13 -1
  39. package/locales/zh-CN/common.json +1 -0
  40. package/locales/zh-TW/chat.json +13 -1
  41. package/locales/zh-TW/common.json +1 -0
  42. package/package.json +4 -6
  43. package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +18 -5
  44. package/src/app/chat/(desktop)/features/ChatInput/index.tsx +1 -1
  45. package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +0 -1
  46. package/src/app/chat/features/Migration/Failed.tsx +2 -0
  47. package/src/app/chat/features/SessionListContent/CollapseGroup/Actions.tsx +115 -0
  48. package/src/app/chat/features/SessionListContent/CollapseGroup/index.tsx +6 -0
  49. package/src/app/chat/features/SessionListContent/DefaultMode.tsx +63 -23
  50. package/src/app/chat/features/SessionListContent/List/AddButton.tsx +3 -3
  51. package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +89 -35
  52. package/src/app/chat/features/SessionListContent/List/Item/index.tsx +35 -15
  53. package/src/app/chat/features/SessionListContent/List/index.tsx +4 -2
  54. package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +84 -0
  55. package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/index.tsx +79 -0
  56. package/src/app/chat/features/SessionListContent/Modals/CreateGroupModal.tsx +63 -0
  57. package/src/app/chat/features/SessionListContent/Modals/RenameGroupModal.tsx +50 -0
  58. package/src/app/chat/features/SessionListContent/SearchMode.tsx +1 -1
  59. package/src/app/settings/common/Common.tsx +5 -1
  60. package/src/components/FullscreenLoading/index.tsx +1 -1
  61. package/src/database/core/__tests__/db-upgrade.test.ts +42 -0
  62. package/src/database/core/db.ts +26 -4
  63. package/src/database/core/schemas.ts +14 -1
  64. package/src/database/core/types/db.ts +2 -0
  65. package/src/database/models/__tests__/message.test.ts +42 -0
  66. package/src/database/models/__tests__/plugin.test.ts +77 -0
  67. package/src/database/models/__tests__/session.test.ts +202 -63
  68. package/src/database/models/__tests__/sessionGroup.test.ts +195 -0
  69. package/src/database/models/__tests__/topic.test.ts +76 -1
  70. package/src/database/models/message.ts +7 -8
  71. package/src/database/models/session.ts +103 -7
  72. package/src/database/models/sessionGroup.ts +76 -0
  73. package/src/database/schemas/session.ts +1 -0
  74. package/src/database/schemas/sessionGroup.ts +8 -0
  75. package/src/features/ChatInput/ActionBar/Clear.tsx +2 -1
  76. package/src/features/Conversation/Actions/Assistant.tsx +1 -1
  77. package/src/features/Conversation/Actions/Function.tsx +2 -2
  78. package/src/features/Conversation/Actions/User.tsx +1 -1
  79. package/src/features/Conversation/hooks/useChatListActionsBar.tsx +3 -3
  80. package/src/features/PluginDevModal/index.tsx +3 -0
  81. package/src/features/PluginStore/PluginItem/Action.tsx +2 -0
  82. package/src/locales/default/chat.ts +13 -1
  83. package/src/locales/default/common.ts +1 -0
  84. package/src/migrations/FromV2ToV3/fixtures/input-v2-session.json +72 -0
  85. package/src/migrations/FromV2ToV3/fixtures/output-v3-from-v1.json +203 -0
  86. package/src/migrations/FromV2ToV3/fixtures/output-v3.json +74 -0
  87. package/src/migrations/FromV2ToV3/index.ts +30 -0
  88. package/src/migrations/FromV2ToV3/migrations.test.ts +42 -0
  89. package/src/migrations/FromV2ToV3/types/v3.ts +27 -0
  90. package/src/migrations/index.ts +7 -1
  91. package/src/services/__tests__/session.test.ts +198 -7
  92. package/src/services/config.ts +23 -7
  93. package/src/services/session.ts +67 -14
  94. package/src/store/global/slices/common/action.test.ts +130 -0
  95. package/src/store/global/slices/common/action.ts +14 -0
  96. package/src/store/global/slices/common/initialState.ts +6 -2
  97. package/src/store/global/slices/common/selectors.ts +1 -4
  98. package/src/store/global/slices/settings/__snapshots__/selectors.test.ts.snap +83 -0
  99. package/src/store/global/slices/settings/action.ts +2 -2
  100. package/src/store/global/slices/settings/selectors.test.ts +132 -182
  101. package/src/store/global/store.ts +7 -0
  102. package/src/store/session/initialState.ts +5 -30
  103. package/src/store/session/selectors.ts +2 -1
  104. package/src/store/session/slices/session/action.test.ts +56 -0
  105. package/src/store/session/slices/session/action.ts +13 -9
  106. package/src/store/session/slices/session/helpers.ts +2 -3
  107. package/src/store/session/slices/session/initialState.ts +31 -23
  108. package/src/store/session/slices/session/selectors/index.ts +0 -1
  109. package/src/store/session/slices/session/selectors/list.ts +11 -18
  110. package/src/store/session/slices/sessionGroup/action.ts +54 -0
  111. package/src/store/session/slices/sessionGroup/initialState.ts +5 -0
  112. package/src/store/session/slices/sessionGroup/selectors.ts +15 -0
  113. package/src/store/session/store.ts +9 -2
  114. package/src/styles/antdOverride.ts +0 -7
  115. package/src/types/exportConfig.ts +3 -1
  116. package/src/types/session.ts +27 -3
  117. package/src/store/session/slices/session/selectors/export.ts +0 -31
  118. /package/docs/Development/{Feature-Development.md → Feature-Development-Frontend.md} +0 -0
  119. /package/docs/Development/{State-Management/State-Management-Intro.md → State-Management-Intro.md} +0 -0
  120. /package/docs/Development/{State-Management/Selectors.md → State-Management-Selectors.md} +0 -0
  121. /package/docs/Development/{State-Management/Selectors.zh-CN.md → State-Management-Selectors.zh-CN.md} +0 -0
@@ -1,25 +1,23 @@
1
1
  import { INBOX_SESSION_ID } from '@/const/session';
2
2
  import { sessionHelpers } from '@/store/session/slices/session/helpers';
3
3
  import { MetaData } from '@/types/meta';
4
- import { LobeAgentSession, LobeSessions, SessionGroupDefaultKeys } from '@/types/session';
4
+ import { CustomSessionGroup, LobeAgentSession, LobeSessions } from '@/types/session';
5
5
 
6
6
  import { SessionStore } from '../../../store';
7
7
  import { initLobeSession } from '../initialState';
8
8
 
9
- const defaultSessions = (s: SessionStore): LobeSessions => s.sessions;
9
+ const defaultSessions = (s: SessionStore): LobeSessions => s.defaultSessions;
10
+ const pinnedSessions = (s: SessionStore): LobeSessions => s.pinnedSessions;
11
+ const customSessionGroups = (s: SessionStore): CustomSessionGroup[] => s.customSessionGroups;
10
12
 
11
- const searchSessions = (s: SessionStore): LobeSessions => s.searchSessions;
12
-
13
- const pinnedSessionList = (s: SessionStore) =>
14
- defaultSessions(s).filter((s) => s.group === SessionGroupDefaultKeys.Pinned);
13
+ const allSessions = (s: SessionStore): LobeSessions => s.sessions;
15
14
 
16
- const unpinnedSessionList = (s: SessionStore) =>
17
- defaultSessions(s).filter((s) => s.group === SessionGroupDefaultKeys.Default);
15
+ const searchSessions = (s: SessionStore): LobeSessions => s.searchSessions;
18
16
 
19
17
  const getSessionById =
20
18
  (id: string) =>
21
19
  (s: SessionStore): LobeAgentSession =>
22
- sessionHelpers.getSessionById(id, s.sessions);
20
+ sessionHelpers.getSessionById(id, allSessions(s));
23
21
 
24
22
  const getSessionMetaById =
25
23
  (id: string) =>
@@ -33,18 +31,13 @@ const getSessionMetaById =
33
31
  const currentSession = (s: SessionStore): LobeAgentSession | undefined => {
34
32
  if (!s.activeId) return;
35
33
 
36
- return s.sessions.find((i) => i.id === s.activeId);
34
+ return allSessions(s).find((i) => i.id === s.activeId);
37
35
  };
38
36
 
39
37
  const currentSessionSafe = (s: SessionStore): LobeAgentSession => {
40
38
  return currentSession(s) || initLobeSession;
41
39
  };
42
40
 
43
- const hasPinnedSessionList = (s: SessionStore) => {
44
- const list = pinnedSessionList(s);
45
- return list?.length > 0;
46
- };
47
-
48
41
  const hasCustomAgents = (s: SessionStore) => defaultSessions(s).length > 0;
49
42
 
50
43
  const isInboxSession = (s: SessionStore) => s.activeId === INBOX_SESSION_ID;
@@ -57,14 +50,14 @@ const isSomeSessionActive = (s: SessionStore) => !!s.activeId && isSessionListIn
57
50
  export const sessionSelectors = {
58
51
  currentSession,
59
52
  currentSessionSafe,
53
+ customSessionGroups,
54
+ defaultSessions,
60
55
  getSessionById,
61
56
  getSessionMetaById,
62
57
  hasCustomAgents,
63
- hasPinnedSessionList,
64
58
  isInboxSession,
65
59
  isSessionListInit,
66
60
  isSomeSessionActive,
67
- pinnedSessionList,
61
+ pinnedSessions,
68
62
  searchSessions,
69
- unpinnedSessionList,
70
63
  };
@@ -0,0 +1,54 @@
1
+ import { StateCreator } from 'zustand/vanilla';
2
+
3
+ import { sessionService } from '@/services/session';
4
+ import { SessionStore } from '@/store/session';
5
+ import { SessionGroupItem } from '@/types/session';
6
+
7
+ export interface SessionGroupAction {
8
+ addSessionGroup: (name: string) => Promise<string>;
9
+ clearSessionGroups: () => Promise<void>;
10
+ removeSessionGroup: (id: string) => Promise<void>;
11
+ updateSessionGroupId: (sessionId: string, groupId: string) => Promise<void>;
12
+ updateSessionGroupName: (id: string, name: string) => Promise<void>;
13
+ updateSessionGroupSort: (items: SessionGroupItem[]) => Promise<void>;
14
+ }
15
+
16
+ export const createSessionGroupSlice: StateCreator<
17
+ SessionStore,
18
+ [['zustand/devtools', never]],
19
+ [],
20
+ SessionGroupAction
21
+ > = (set, get) => ({
22
+ addSessionGroup: async (name) => {
23
+ const id = await sessionService.createSessionGroup(name);
24
+
25
+ await get().refreshSessions();
26
+
27
+ return id;
28
+ },
29
+
30
+ clearSessionGroups: async () => {
31
+ await sessionService.clearSessionGroups();
32
+ await get().refreshSessions();
33
+ },
34
+
35
+ removeSessionGroup: async (id) => {
36
+ await sessionService.removeSessionGroup(id);
37
+ await get().refreshSessions();
38
+ },
39
+ updateSessionGroupId: async (sessionId, groupId) => {
40
+ await sessionService.updateSessionGroupId(sessionId, groupId);
41
+
42
+ await get().refreshSessions();
43
+ },
44
+
45
+ updateSessionGroupName: async (id, name) => {
46
+ await sessionService.updateSessionGroup(id, { name });
47
+ await get().refreshSessions();
48
+ },
49
+ updateSessionGroupSort: async (items) => {
50
+ const sortMap = items.map((item, index) => ({ id: item.id, sort: index }));
51
+ await sessionService.updateSessionGroupOrder(sortMap);
52
+ await get().refreshSessions();
53
+ },
54
+ });
@@ -0,0 +1,5 @@
1
+ export interface SessionGroupState {
2
+ activeGroupId?: string;
3
+ }
4
+
5
+ export const initSessionGroupState: SessionGroupState = {};
@@ -0,0 +1,15 @@
1
+ import { SessionStore } from '@/store/session';
2
+
3
+ const sessionGroupItems = (s: SessionStore) =>
4
+ s.customSessionGroups.map((group) => ({
5
+ id: group.id,
6
+ name: group.name,
7
+ }));
8
+
9
+ const getGroupById = (id: string) => (s: SessionStore) =>
10
+ sessionGroupItems(s).find((group) => group.id === id);
11
+
12
+ export const sessionGroupSelectors = {
13
+ getGroupById,
14
+ sessionGroupItems,
15
+ };
@@ -8,17 +8,24 @@ import { isDev } from '@/utils/env';
8
8
  import { SessionStoreState, initialState } from './initialState';
9
9
  import { AgentAction, createAgentSlice } from './slices/agent/action';
10
10
  import { SessionAction, createSessionSlice } from './slices/session/action';
11
+ import { SessionGroupAction, createSessionGroupSlice } from './slices/sessionGroup/action';
11
12
 
12
13
  // =============== 聚合 createStoreFn ============ //
13
14
 
14
- export type SessionStore = SessionAction & AgentAction & SessionStoreState;
15
+ export interface SessionStore
16
+ extends SessionAction,
17
+ AgentAction,
18
+ SessionGroupAction,
19
+ SessionStoreState {}
20
+
15
21
  const createStore: StateCreator<SessionStore, [['zustand/devtools', never]]> = (...parameters) => ({
16
22
  ...initialState,
17
23
  ...createAgentSlice(...parameters),
18
24
  ...createSessionSlice(...parameters),
25
+ ...createSessionGroupSlice(...parameters),
19
26
  });
20
27
 
21
- // =============== 实装 useStore ============ //
28
+ // =============== implement useStore ============ //
22
29
 
23
30
  export const useSessionStore = createWithEqualityFn<SessionStore>()(
24
31
  subscribeWithSelector(
@@ -4,11 +4,4 @@ export default ({ token }: { prefixCls: string; token: Theme }) => css`
4
4
  .${token.prefixCls}-popover {
5
5
  z-index: 1100;
6
6
  }
7
- .${token.prefixCls}-dropdown {
8
- > ul {
9
- border: 1px solid ${token.colorBorderSecondary};
10
- border-radius: ${token.borderRadius}px !important;
11
- box-shadow: ${token.boxShadowSecondary};
12
- }
13
- }
14
7
  `;
@@ -1,5 +1,5 @@
1
1
  import { ChatMessage } from '@/types/message';
2
- import { LobeSessions } from '@/types/session';
2
+ import { LobeSessions, SessionGroupItem } from '@/types/session';
3
3
  import { GlobalSettings } from '@/types/settings';
4
4
  import { ChatTopic } from '@/types/topic';
5
5
 
@@ -43,6 +43,7 @@ export interface ConfigModelMap {
43
43
  */
44
44
  export interface ConfigStateSessions {
45
45
  messages: ChatMessage[];
46
+ sessionGroups: SessionGroupItem[];
46
47
  sessions: LobeSessions;
47
48
  topics: ChatTopic[];
48
49
  }
@@ -60,6 +61,7 @@ export interface ConfigStateSingleSession {
60
61
  * 配置状态:角色
61
62
  */
62
63
  export interface ConfigStateAgents {
64
+ sessionGroups: SessionGroupItem[];
63
65
  sessions: LobeSessions;
64
66
  }
65
67
 
@@ -7,19 +7,30 @@ export enum LobeSessionType {
7
7
  Group = 'group',
8
8
  }
9
9
 
10
- export type SessionGroupKey = 'pinned' | 'default' | string;
10
+ export type SessionGroupId = SessionDefaultGroup | string;
11
11
 
12
- export enum SessionGroupDefaultKeys {
12
+ export enum SessionDefaultGroup {
13
13
  Default = 'default',
14
14
  Pinned = 'pinned',
15
15
  }
16
16
 
17
+ export interface SessionGroupItem {
18
+ createdAt: number;
19
+ id: string;
20
+ name: string;
21
+ sort?: number;
22
+ updatedAt: number;
23
+ }
24
+
25
+ export type SessionGroups = SessionGroupItem[];
26
+
17
27
  /**
18
28
  * Lobe Agent
19
29
  */
20
30
  export interface LobeAgentSession extends BaseDataModel {
21
31
  config: LobeAgentConfig;
22
- group?: SessionGroupKey;
32
+ group?: SessionGroupId;
33
+ pinned?: boolean;
23
34
  type: LobeSessionType.Agent;
24
35
  }
25
36
 
@@ -32,3 +43,16 @@ export interface LobeAgentSettings {
32
43
  }
33
44
 
34
45
  export type LobeSessions = LobeAgentSession[];
46
+
47
+ export interface CustomSessionGroup {
48
+ children: LobeSessions;
49
+ id: SessionGroupId;
50
+ name: string;
51
+ }
52
+
53
+ export interface ChatSessionList {
54
+ all: LobeSessions;
55
+ customGroup: CustomSessionGroup[];
56
+ default: LobeSessions;
57
+ pinned: LobeSessions;
58
+ }
@@ -1,31 +0,0 @@
1
- import { transform } from 'lodash-es';
2
-
3
- import { SessionStore } from '@/store/session';
4
- import { ConfigStateAgents, ConfigStateSessions } from '@/types/exportConfig';
5
- import { LobeAgentSession, LobeSessions } from '@/types/session';
6
-
7
- import { sessionHelpers } from '../helpers';
8
-
9
- const exportSessions = (s: SessionStore): Pick<ConfigStateSessions, 'sessions'> => ({
10
- sessions: s.sessions,
11
- });
12
-
13
- const exportAgents = (s: SessionStore): ConfigStateAgents => {
14
- return {
15
- sessions: transform(s.sessions, (result: LobeSessions, value, key) => {
16
- // 移除 chats 和 topics
17
- result[key] = { ...value, chats: {}, topics: {} } as LobeAgentSession;
18
- }),
19
- };
20
- };
21
-
22
- const getExportAgent =
23
- (id: string) =>
24
- (s: SessionStore): LobeAgentSession =>
25
- sessionHelpers.getSessionById(id, s.sessions);
26
-
27
- export const sessionExportSelectors = {
28
- exportAgents,
29
- exportSessions,
30
- getExportAgent,
31
- };