@lobehub/chat 0.28.0 → 0.30.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 (40) hide show
  1. package/.eslintrc.js +3 -0
  2. package/CHANGELOG.md +54 -0
  3. package/package.json +1 -1
  4. package/src/const/message.ts +2 -0
  5. package/src/features/SideBar/BottomAction.tsx +1 -1
  6. package/src/helpers/export.ts +62 -0
  7. package/src/hooks/useExportConfig.ts +3 -66
  8. package/src/locales/default/common.ts +2 -0
  9. package/src/locales/default/setting.ts +4 -3
  10. package/src/migrations/index.ts +2 -2
  11. package/src/pages/chat/SessionList/List/SessionItem.tsx +44 -52
  12. package/src/pages/chat/SessionList/List/style.ts +3 -0
  13. package/src/pages/chat/[id]/Config/SideBar.tsx +1 -1
  14. package/src/pages/chat/[id]/Config/Topic/DefaultContent.tsx +29 -0
  15. package/src/pages/chat/[id]/Config/Topic/TopicContent.tsx +151 -0
  16. package/src/pages/chat/[id]/Config/Topic/TopicItem.tsx +70 -0
  17. package/src/pages/chat/[id]/Config/{Topic.tsx → Topic/index.tsx} +1 -8
  18. package/src/pages/chat/[id]/Config/index.tsx +3 -5
  19. package/src/pages/chat/[id]/Conversation/Input/index.tsx +7 -10
  20. package/src/pages/chat/[id]/Header.tsx +3 -2
  21. package/src/pages/chat/[id]/edit/Header.tsx +14 -1
  22. package/src/pages/setting/SettingForm.tsx +34 -29
  23. package/src/store/session/slices/agentConfig/action.ts +0 -11
  24. package/src/store/session/slices/agentConfig/initialState.ts +0 -4
  25. package/src/store/session/slices/chat/actions/message.ts +2 -2
  26. package/src/store/session/slices/chat/actions/topic.ts +28 -2
  27. package/src/store/session/slices/chat/initialState.ts +1 -0
  28. package/src/store/session/slices/chat/reducers/topic.ts +23 -1
  29. package/src/store/session/slices/chat/selectors/index.ts +2 -1
  30. package/src/store/session/slices/chat/selectors/topic.ts +8 -0
  31. package/src/store/session/slices/session/action.ts +8 -5
  32. package/src/store/session/slices/session/selectors/export.ts +23 -0
  33. package/src/store/session/slices/session/selectors/index.ts +6 -2
  34. package/src/store/session/slices/session/selectors/list.ts +3 -2
  35. package/src/store/settings/action.ts +11 -1
  36. package/src/store/settings/initialState.ts +2 -0
  37. package/src/store/settings/selectors.ts +10 -0
  38. package/src/types/exportConfig.ts +34 -14
  39. package/src/utils/config.ts +54 -1
  40. package/src/pages/chat/[id]/Config/TopicItem.tsx +0 -136
@@ -36,6 +36,7 @@ export interface SettingsAction {
36
36
  * @param key - 选中的侧边栏选项
37
37
  */
38
38
  switchSideBar: (key: SidebarTabKey) => void;
39
+ toggleAgentPanel: (visible?: boolean) => void;
39
40
  }
40
41
 
41
42
  export const createSettings: StateCreator<
@@ -49,7 +50,11 @@ export const createSettings: StateCreator<
49
50
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
50
51
  const { OPENAI_API_KEY: _, password: __, ...settings } = importSettings;
51
52
 
52
- setSettings(settings);
53
+ setSettings({
54
+ ...settings,
55
+ // 如果用户存在用户头像,那么不做导入
56
+ avatar: !get().settings.avatar ? settings.avatar : get().settings.avatar,
57
+ });
53
58
  },
54
59
  resetSettings: () => {
55
60
  set({ settings: DEFAULT_SETTINGS });
@@ -67,4 +72,9 @@ export const createSettings: StateCreator<
67
72
  switchSideBar: (key) => {
68
73
  set({ sidebarKey: key });
69
74
  },
75
+ toggleAgentPanel: (newValue) => {
76
+ const showAgentConfig = typeof newValue === 'boolean' ? newValue : !get().showAgentConfig;
77
+
78
+ set({ showAgentConfig });
79
+ },
70
80
  });
@@ -31,6 +31,7 @@ export interface SettingsState {
31
31
  sessionExpandable?: boolean;
32
32
  sessionsWidth: number;
33
33
  settings: GlobalSettings;
34
+ showAgentConfig?: boolean;
34
35
  sidebarKey: SidebarTabKey;
35
36
  }
36
37
 
@@ -39,5 +40,6 @@ export const initialState: SettingsState = {
39
40
  sessionExpandable: true,
40
41
  sessionsWidth: 320,
41
42
  settings: DEFAULT_SETTINGS,
43
+ showAgentConfig: true,
42
44
  sidebarKey: 'chat',
43
45
  };
@@ -1,6 +1,7 @@
1
1
  import { defaults } from 'lodash-es';
2
2
 
3
3
  import { DEFAULT_SETTINGS } from '@/store/settings/initialState';
4
+ import { GlobalSettings } from '@/types/settings';
4
5
 
5
6
  import { SettingsStore } from './store';
6
7
 
@@ -11,7 +12,16 @@ const selecThemeMode = (s: SettingsStore) => ({
11
12
  themeMode: s.settings.themeMode,
12
13
  });
13
14
 
15
+ export const exportSettings = (s: SettingsStore) => {
16
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
+ const { OPENAI_API_KEY: _, password: __, ...settings } = s.settings;
18
+
19
+ return settings as GlobalSettings;
20
+ };
21
+
14
22
  export const settingsSelectors = {
15
23
  currentSettings,
24
+ exportSettings,
25
+
16
26
  selecThemeMode,
17
27
  };
@@ -1,41 +1,61 @@
1
1
  import { LobeSessions } from '@/types/session';
2
2
  import { GlobalSettings } from '@/types/settings';
3
3
 
4
- export interface ConfigState {
4
+ // 存在4种导出方式
5
+ export type ExportType = 'agents' | 'sessions' | 'settings' | 'all';
6
+
7
+ // 4种方式对应的 state
8
+ export interface ConfigStateAll {
5
9
  sessions: LobeSessions;
6
10
  settings: GlobalSettings;
7
11
  }
8
-
9
- export interface SettingsConfigState {
12
+ export interface ConfigStateSettings {
10
13
  settings: GlobalSettings;
11
14
  }
12
- export interface SessionsConfigState {
15
+ export interface ConfigStateSessions {
13
16
  sessions: LobeSessions;
14
17
  }
15
18
 
16
- export type ExportType = 'agents' | 'sessions' | 'settings' | 'all';
17
-
18
- export type ConfigFile = ConfigFileSettings | ConfigFileSessions | ConfigFileAll | ConfigFileAgents;
19
-
19
+ // 4种方式对应的 file
20
20
  export interface ConfigFileSettings {
21
21
  exportType: 'settings';
22
- state: SettingsConfigState;
22
+ state: ConfigStateSettings;
23
23
  version: number;
24
24
  }
25
25
  export interface ConfigFileSessions {
26
26
  exportType: 'sessions';
27
- state: SessionsConfigState;
27
+ state: ConfigStateSessions;
28
28
  version: number;
29
29
  }
30
-
31
30
  export interface ConfigFileAgents {
32
31
  exportType: 'agents';
33
- state: SessionsConfigState;
32
+ state: ConfigStateSessions;
34
33
  version: number;
35
34
  }
36
-
37
35
  export interface ConfigFileAll {
38
36
  exportType: 'all';
39
- state: ConfigState;
37
+ state: ConfigStateAll;
40
38
  version: number;
41
39
  }
40
+
41
+ export type ConfigFile = ConfigFileSettings | ConfigFileSessions | ConfigFileAll | ConfigFileAgents;
42
+
43
+ // 用于 map 收集类型的 map
44
+ export interface ConfigModelMap {
45
+ agents: {
46
+ file: ConfigFileAgents;
47
+ state: ConfigStateSessions;
48
+ };
49
+ all: {
50
+ file: ConfigFileAll;
51
+ state: ConfigStateAll;
52
+ };
53
+ sessions: {
54
+ file: ConfigFileSessions;
55
+ state: ConfigStateSessions;
56
+ };
57
+ settings: {
58
+ file: ConfigFileSettings;
59
+ state: ConfigStateSettings;
60
+ };
61
+ }
@@ -1,7 +1,15 @@
1
1
  import { notification } from 'antd';
2
2
 
3
3
  import { CURRENT_CONFIG_VERSION, Migration } from '@/migrations';
4
- import { ConfigFile } from '@/types/exportConfig';
4
+ import {
5
+ ConfigFile,
6
+ ConfigFileAgents,
7
+ ConfigFileAll,
8
+ ConfigFileSessions,
9
+ ConfigFileSettings,
10
+ ConfigModelMap,
11
+ ExportType,
12
+ } from '@/types/exportConfig';
5
13
 
6
14
  export const exportConfigFile = (config: object, fileName?: string) => {
7
15
  const file = `LobeChat-${fileName || '-config'}-v${CURRENT_CONFIG_VERSION}.json`;
@@ -48,3 +56,48 @@ export const importConfigFile = (info: any, onConfigImport: (config: ConfigFile)
48
56
  //@ts-ignore file 类型不明确
49
57
  reader.readAsText(info.file.originFileObj, 'utf8');
50
58
  };
59
+
60
+ type CreateConfigFileState<T extends ExportType> = ConfigModelMap[T]['state'];
61
+
62
+ type CreateConfigFile<T extends ExportType> = ConfigModelMap[T]['file'];
63
+
64
+ export const createConfigFile = <T extends ExportType>(
65
+ type: T,
66
+ state: CreateConfigFileState<T>,
67
+ ): CreateConfigFile<T> => {
68
+ switch (type) {
69
+ case 'agents': {
70
+ return {
71
+ exportType: 'agents',
72
+ state,
73
+ version: Migration.targetVersion,
74
+ } as ConfigFileAgents;
75
+ }
76
+
77
+ case 'sessions': {
78
+ return {
79
+ exportType: 'sessions',
80
+ state,
81
+ version: Migration.targetVersion,
82
+ } as ConfigFileSessions;
83
+ }
84
+
85
+ case 'settings': {
86
+ return {
87
+ exportType: 'settings',
88
+ state,
89
+ version: Migration.targetVersion,
90
+ } as ConfigFileSettings;
91
+ }
92
+
93
+ case 'all': {
94
+ return {
95
+ exportType: 'all',
96
+ state,
97
+ version: Migration.targetVersion,
98
+ } as ConfigFileAll;
99
+ }
100
+ }
101
+
102
+ throw new Error('缺少正确的导出类型,请检查实现...');
103
+ };
@@ -1,136 +0,0 @@
1
- import { ActionIcon, Icon } from '@lobehub/ui';
2
- import { Dropdown, type MenuProps, Tag, Typography } from 'antd';
3
- import { createStyles } from 'antd-style';
4
- import { MessageSquareDashed, MoreVertical, PencilLine, Share2, Star, Trash } from 'lucide-react';
5
- import { memo, useMemo, useState } from 'react';
6
- import { useTranslation } from 'react-i18next';
7
- import { Flexbox } from 'react-layout-kit';
8
- import { shallow } from 'zustand/shallow';
9
-
10
- import { useSessionStore } from '@/store/session';
11
-
12
- const { Paragraph } = Typography;
13
-
14
- const useStyles = createStyles(({ css, token, isDarkMode }) => ({
15
- active: css`
16
- background: ${isDarkMode ? token.colorFillTertiary : token.colorFillSecondary};
17
- transition: background 200ms ${token.motionEaseOut};
18
-
19
- &:hover {
20
- background: ${token.colorFill};
21
- }
22
- `,
23
- container: css`
24
- cursor: pointer;
25
- padding: 12px 8px;
26
- border-radius: ${token.borderRadius}px;
27
-
28
- .topic-more {
29
- opacity: 0;
30
- transition: opacity 400ms ${token.motionEaseOut};
31
- }
32
-
33
- &:hover {
34
- background: ${token.colorFillSecondary};
35
-
36
- .topic-more {
37
- opacity: 1;
38
- }
39
- }
40
- `,
41
- split: css`
42
- border-bottom: 1px solid ${token.colorSplit};
43
- `,
44
- }));
45
-
46
- export interface ConfigCellProps {
47
- active?: boolean;
48
- fav?: boolean;
49
- id?: string;
50
- showFav?: boolean;
51
- title: string;
52
- }
53
-
54
- const TopicItem = memo<ConfigCellProps>(({ title, active, id, showFav, fav }) => {
55
- const [dropdownOpen, setDropdownOpen] = useState(false);
56
- const { styles, theme, cx } = useStyles();
57
- const { t } = useTranslation('common');
58
-
59
- const [dispatchTopic, toggleTopic] = useSessionStore(
60
- (s) => [s.dispatchTopic, s.toggleTopic],
61
- shallow,
62
- );
63
-
64
- // TODO: 动作绑定
65
- const items: MenuProps['items'] = useMemo(
66
- () => [
67
- {
68
- icon: <Icon icon={PencilLine} />,
69
- key: 'rename',
70
- label: t('rename'),
71
- },
72
- {
73
- icon: <Icon icon={Share2} />,
74
- key: 'share',
75
- label: t('share'),
76
- },
77
- {
78
- icon: <Icon icon={Trash} />,
79
- key: 'delete',
80
- label: t('delete'),
81
- },
82
- ],
83
- [],
84
- );
85
-
86
- return (
87
- <Flexbox
88
- align={'center'}
89
- className={cx(styles.container, active && styles.active)}
90
- distribution={'space-between'}
91
- horizontal
92
- onClick={() => {
93
- toggleTopic(id);
94
- }}
95
- >
96
- <Flexbox align={'center'} gap={8} horizontal>
97
- {!showFav ? (
98
- <Flexbox align={'center'} height={24} justify={'center'} width={24}>
99
- <Icon color={theme.colorTextDescription} icon={MessageSquareDashed} />
100
- </Flexbox>
101
- ) : (
102
- <ActionIcon
103
- color={fav ? theme.colorWarning : undefined}
104
- fill={fav ? theme.colorWarning : 'transparent'}
105
- icon={Star}
106
- onClick={() => {
107
- if (!id) return;
108
- dispatchTopic({ id, key: 'favorite', type: 'updateChatTopic', value: !fav });
109
- }}
110
- size={'small'}
111
- />
112
- )}
113
- <Paragraph ellipsis={{ rows: 2 }} style={{ margin: 0 }}>
114
- {title}
115
- </Paragraph>
116
- {id ? '' : <Tag>{t('temp')}</Tag>}
117
- </Flexbox>
118
- <Dropdown
119
- arrow={false}
120
- menu={{ items }}
121
- onOpenChange={setDropdownOpen}
122
- open={dropdownOpen}
123
- trigger={['click']}
124
- >
125
- <ActionIcon
126
- className="topic-more"
127
- icon={MoreVertical}
128
- size={'small'}
129
- style={dropdownOpen ? { opacity: 1 } : {}}
130
- />
131
- </Dropdown>
132
- </Flexbox>
133
- );
134
- });
135
-
136
- export default TopicItem;