@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.
- package/.github/ISSUE_TEMPLATE/2_feature_request_cn.yml +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +9 -9
- package/README.zh-CN.md +9 -9
- package/docs/Development/Feature-Development-Frontend.zh-CN.md +126 -0
- package/docs/Development/Feature-Development.zh-CN.md +664 -83
- package/docs/Development/{State-Management/State-Management-Intro.zh-CN.md → State-Management-Intro.zh-CN.md} +9 -9
- package/docs/Home.md +1 -0
- package/docs/_Sidebar.md +1 -0
- package/locales/ar/chat.json +13 -1
- package/locales/ar/common.json +1 -0
- package/locales/de-DE/chat.json +13 -1
- package/locales/de-DE/common.json +1 -0
- package/locales/en-US/chat.json +13 -1
- package/locales/en-US/common.json +1 -0
- package/locales/es-ES/chat.json +13 -1
- package/locales/es-ES/common.json +1 -0
- package/locales/fr-FR/chat.json +13 -1
- package/locales/fr-FR/common.json +1 -0
- package/locales/it-IT/chat.json +13 -1
- package/locales/it-IT/common.json +1 -0
- package/locales/ja-JP/chat.json +13 -1
- package/locales/ja-JP/common.json +1 -0
- package/locales/ko-KR/chat.json +13 -1
- package/locales/ko-KR/common.json +1 -0
- package/locales/nl-NL/chat.json +13 -1
- package/locales/nl-NL/common.json +1 -0
- package/locales/pl-PL/chat.json +13 -1
- package/locales/pl-PL/common.json +1 -0
- package/locales/pt-BR/chat.json +13 -1
- package/locales/pt-BR/common.json +1 -0
- package/locales/ru-RU/chat.json +13 -1
- package/locales/ru-RU/common.json +1 -0
- package/locales/tr-TR/chat.json +13 -1
- package/locales/tr-TR/common.json +1 -0
- package/locales/vi-VN/chat.json +13 -1
- package/locales/vi-VN/common.json +1 -0
- package/locales/zh-CN/chat.json +13 -1
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-TW/chat.json +13 -1
- package/locales/zh-TW/common.json +1 -0
- package/package.json +4 -6
- package/src/app/chat/(desktop)/features/ChatInput/Footer/index.tsx +18 -5
- package/src/app/chat/(desktop)/features/ChatInput/index.tsx +1 -1
- package/src/app/chat/features/ChatHeader/ShareButton/index.tsx +0 -1
- package/src/app/chat/features/Migration/Failed.tsx +2 -0
- package/src/app/chat/features/SessionListContent/CollapseGroup/Actions.tsx +115 -0
- package/src/app/chat/features/SessionListContent/CollapseGroup/index.tsx +6 -0
- package/src/app/chat/features/SessionListContent/DefaultMode.tsx +63 -23
- package/src/app/chat/features/SessionListContent/List/AddButton.tsx +3 -3
- package/src/app/chat/features/SessionListContent/List/Item/Actions.tsx +89 -35
- package/src/app/chat/features/SessionListContent/List/Item/index.tsx +35 -15
- package/src/app/chat/features/SessionListContent/List/index.tsx +4 -2
- package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/GroupItem.tsx +84 -0
- package/src/app/chat/features/SessionListContent/Modals/ConfigGroupModal/index.tsx +79 -0
- package/src/app/chat/features/SessionListContent/Modals/CreateGroupModal.tsx +63 -0
- package/src/app/chat/features/SessionListContent/Modals/RenameGroupModal.tsx +50 -0
- package/src/app/chat/features/SessionListContent/SearchMode.tsx +1 -1
- package/src/app/settings/common/Common.tsx +5 -1
- package/src/components/FullscreenLoading/index.tsx +1 -1
- package/src/database/core/__tests__/db-upgrade.test.ts +42 -0
- package/src/database/core/db.ts +26 -4
- package/src/database/core/schemas.ts +14 -1
- package/src/database/core/types/db.ts +2 -0
- package/src/database/models/__tests__/message.test.ts +42 -0
- package/src/database/models/__tests__/plugin.test.ts +77 -0
- package/src/database/models/__tests__/session.test.ts +202 -63
- package/src/database/models/__tests__/sessionGroup.test.ts +195 -0
- package/src/database/models/__tests__/topic.test.ts +76 -1
- package/src/database/models/message.ts +7 -8
- package/src/database/models/session.ts +103 -7
- package/src/database/models/sessionGroup.ts +76 -0
- package/src/database/schemas/session.ts +1 -0
- package/src/database/schemas/sessionGroup.ts +8 -0
- package/src/features/ChatInput/ActionBar/Clear.tsx +2 -1
- package/src/features/Conversation/Actions/Assistant.tsx +1 -1
- package/src/features/Conversation/Actions/Function.tsx +2 -2
- package/src/features/Conversation/Actions/User.tsx +1 -1
- package/src/features/Conversation/hooks/useChatListActionsBar.tsx +3 -3
- package/src/features/PluginDevModal/index.tsx +3 -0
- package/src/features/PluginStore/PluginItem/Action.tsx +2 -0
- package/src/locales/default/chat.ts +13 -1
- package/src/locales/default/common.ts +1 -0
- package/src/migrations/FromV2ToV3/fixtures/input-v2-session.json +72 -0
- package/src/migrations/FromV2ToV3/fixtures/output-v3-from-v1.json +203 -0
- package/src/migrations/FromV2ToV3/fixtures/output-v3.json +74 -0
- package/src/migrations/FromV2ToV3/index.ts +30 -0
- package/src/migrations/FromV2ToV3/migrations.test.ts +42 -0
- package/src/migrations/FromV2ToV3/types/v3.ts +27 -0
- package/src/migrations/index.ts +7 -1
- package/src/services/__tests__/session.test.ts +198 -7
- package/src/services/config.ts +23 -7
- package/src/services/session.ts +67 -14
- package/src/store/global/slices/common/action.test.ts +130 -0
- package/src/store/global/slices/common/action.ts +14 -0
- package/src/store/global/slices/common/initialState.ts +6 -2
- package/src/store/global/slices/common/selectors.ts +1 -4
- package/src/store/global/slices/settings/__snapshots__/selectors.test.ts.snap +83 -0
- package/src/store/global/slices/settings/action.ts +2 -2
- package/src/store/global/slices/settings/selectors.test.ts +132 -182
- package/src/store/global/store.ts +7 -0
- package/src/store/session/initialState.ts +5 -30
- package/src/store/session/selectors.ts +2 -1
- package/src/store/session/slices/session/action.test.ts +56 -0
- package/src/store/session/slices/session/action.ts +13 -9
- package/src/store/session/slices/session/helpers.ts +2 -3
- package/src/store/session/slices/session/initialState.ts +31 -23
- package/src/store/session/slices/session/selectors/index.ts +0 -1
- package/src/store/session/slices/session/selectors/list.ts +11 -18
- package/src/store/session/slices/sessionGroup/action.ts +54 -0
- package/src/store/session/slices/sessionGroup/initialState.ts +5 -0
- package/src/store/session/slices/sessionGroup/selectors.ts +15 -0
- package/src/store/session/store.ts +9 -2
- package/src/styles/antdOverride.ts +0 -7
- package/src/types/exportConfig.ts +3 -1
- package/src/types/session.ts +27 -3
- package/src/store/session/slices/session/selectors/export.ts +0 -31
- /package/docs/Development/{Feature-Development.md → Feature-Development-Frontend.md} +0 -0
- /package/docs/Development/{State-Management/State-Management-Intro.md → State-Management-Intro.md} +0 -0
- /package/docs/Development/{State-Management/Selectors.md → State-Management-Selectors.md} +0 -0
- /package/docs/Development/{State-Management/Selectors.zh-CN.md → State-Management-Selectors.zh-CN.md} +0 -0
package/locales/zh-TW/chat.json
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"confirmClearCurrentMessages": "即將清空當前對話,清空後將無法找回,請確認你的操作",
|
|
7
7
|
"confirmRemoveSessionItemAlert": "即將刪除該助手,刪除後將無法找回,請確認你的操作",
|
|
8
8
|
"defaultAgent": "自定義助手",
|
|
9
|
+
"defaultList": "預設清單",
|
|
9
10
|
"defaultSession": "自定義助手",
|
|
10
11
|
"duplicateTitle": "{{title}} 副本",
|
|
11
12
|
"historyRange": "歷史範圍",
|
|
@@ -34,7 +35,18 @@
|
|
|
34
35
|
"roleAndArchive": "角色與記錄",
|
|
35
36
|
"searchAgentPlaceholder": "搜索助手和對話...",
|
|
36
37
|
"sendPlaceholder": "輸入聊天內容...",
|
|
37
|
-
"
|
|
38
|
+
"sessionGroup": {
|
|
39
|
+
"config": "分組管理",
|
|
40
|
+
"confirmRemoveGroupAlert": "即將刪除該分組,刪除後該分組的助手將移動到預設清單,請確認你的操作",
|
|
41
|
+
"createGroup": "新增新分組",
|
|
42
|
+
"createSuccess": "創建成功",
|
|
43
|
+
"inputPlaceholder": "請輸入分組名稱...",
|
|
44
|
+
"moveGroup": "移動到分組",
|
|
45
|
+
"newGroup": "新分組",
|
|
46
|
+
"rename": "重新命名分組",
|
|
47
|
+
"renameSuccess": "重新命名成功",
|
|
48
|
+
"tooLong": "分組名稱長度需在 1-20 之內"
|
|
49
|
+
},
|
|
38
50
|
"shareModal": {
|
|
39
51
|
"download": "下載截圖",
|
|
40
52
|
"imageType": "圖片格式",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.121.0",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -44,11 +44,8 @@
|
|
|
44
44
|
"release": "semantic-release",
|
|
45
45
|
"start": "next start",
|
|
46
46
|
"stylelint": "stylelint \"src/**/*.{js,jsx,ts,tsx}\" --fix",
|
|
47
|
-
"test": "
|
|
48
|
-
"test
|
|
49
|
-
"test-temp-fixture-ci": "echo 'please remove this script after new bun@1.0.24 released' && rm -rf node_modules && npx bun@1.0.22 install",
|
|
50
|
-
"test:coverage": "npm run test-temp-fixture-ci && vitest run --coverage",
|
|
51
|
-
"test:coverage-local": "vitest run --coverage",
|
|
47
|
+
"test": "vitest",
|
|
48
|
+
"test:coverage": "vitest run --coverage",
|
|
52
49
|
"test:update": "vitest -u",
|
|
53
50
|
"type-check": "tsc --noEmit",
|
|
54
51
|
"workflow:docs": "tsx scripts/docsWorkflow/index.ts",
|
|
@@ -161,6 +158,7 @@
|
|
|
161
158
|
"@types/uuid": "^9",
|
|
162
159
|
"@umijs/lint": "^4",
|
|
163
160
|
"@vitest/coverage-v8": "^1",
|
|
161
|
+
"ajv-keywords": "^5.1.0",
|
|
164
162
|
"commitlint": "^18",
|
|
165
163
|
"consola": "^3",
|
|
166
164
|
"dpdm": "^3",
|
|
@@ -4,12 +4,13 @@ import { createStyles } from 'antd-style';
|
|
|
4
4
|
import {
|
|
5
5
|
ChevronUp,
|
|
6
6
|
CornerDownLeft,
|
|
7
|
-
Loader2,
|
|
8
7
|
LucideCheck,
|
|
9
8
|
LucideChevronDown,
|
|
10
9
|
LucideCommand,
|
|
11
10
|
LucidePlus,
|
|
11
|
+
StopCircle,
|
|
12
12
|
} from 'lucide-react';
|
|
13
|
+
import { rgba } from 'polished';
|
|
13
14
|
import { memo } from 'react';
|
|
14
15
|
import { useTranslation } from 'react-i18next';
|
|
15
16
|
import { Center, Flexbox } from 'react-layout-kit';
|
|
@@ -25,7 +26,7 @@ import { isMacOS } from '@/utils/platform';
|
|
|
25
26
|
|
|
26
27
|
import { LocalFiles } from './LocalFiles';
|
|
27
28
|
|
|
28
|
-
const useStyles = createStyles(({ css, prefixCls }) => {
|
|
29
|
+
const useStyles = createStyles(({ css, prefixCls, token }) => {
|
|
29
30
|
return {
|
|
30
31
|
arrow: css`
|
|
31
32
|
&.${prefixCls}-btn.${prefixCls}-btn-icon-only {
|
|
@@ -38,13 +39,19 @@ const useStyles = createStyles(({ css, prefixCls }) => {
|
|
|
38
39
|
align-items: center;
|
|
39
40
|
justify-content: center;
|
|
40
41
|
}
|
|
42
|
+
|
|
43
|
+
.${prefixCls}-btn.${prefixCls}-dropdown-trigger {
|
|
44
|
+
&::before {
|
|
45
|
+
background-color: ${rgba(token.colorBgLayout, 0.1)} !important;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
41
48
|
`,
|
|
42
49
|
};
|
|
43
50
|
});
|
|
44
51
|
|
|
45
52
|
const isMac = isMacOS();
|
|
46
53
|
|
|
47
|
-
const Footer = memo(() => {
|
|
54
|
+
const Footer = memo<{ setExpand?: (expand: boolean) => void }>(({ setExpand }) => {
|
|
48
55
|
const { t } = useTranslation('chat');
|
|
49
56
|
|
|
50
57
|
const { theme, styles } = useStyles();
|
|
@@ -104,12 +111,18 @@ const Footer = memo(() => {
|
|
|
104
111
|
<SaveTopic />
|
|
105
112
|
<Flexbox style={{ minWidth: 92 }}>
|
|
106
113
|
{loading ? (
|
|
107
|
-
<Button icon={loading && <Icon icon={
|
|
114
|
+
<Button icon={loading && <Icon icon={StopCircle} />} onClick={stopGenerateMessage}>
|
|
108
115
|
{t('input.stop')}
|
|
109
116
|
</Button>
|
|
110
117
|
) : (
|
|
111
118
|
<Space.Compact>
|
|
112
|
-
<Button
|
|
119
|
+
<Button
|
|
120
|
+
onClick={() => {
|
|
121
|
+
sendMessage();
|
|
122
|
+
setExpand?.(false);
|
|
123
|
+
}}
|
|
124
|
+
type={'primary'}
|
|
125
|
+
>
|
|
113
126
|
{t('input.send')}
|
|
114
127
|
</Button>
|
|
115
128
|
<Dropdown
|
|
@@ -33,7 +33,6 @@ const ShareButton = memo<ShareButtonProps>(({ mobile, setOpen, open }) => {
|
|
|
33
33
|
size={mobile ? MOBILE_HEADER_ICON_SIZE : DESKTOP_HEADER_ICON_SIZE}
|
|
34
34
|
title={t('share')}
|
|
35
35
|
/>
|
|
36
|
-
|
|
37
36
|
<ShareModal onCancel={() => setIsModalOpen(false)} open={isModalOpen} />
|
|
38
37
|
</>
|
|
39
38
|
);
|
|
@@ -63,8 +63,10 @@ const Failed = memo<FailedProps>(({ error, state, setUpgradeStatus, setError, up
|
|
|
63
63
|
>
|
|
64
64
|
<ExportConfigButton primary state={state} />
|
|
65
65
|
<Popconfirm
|
|
66
|
+
arrow={false}
|
|
66
67
|
okButtonProps={{
|
|
67
68
|
danger: true,
|
|
69
|
+
type: 'primary',
|
|
68
70
|
}}
|
|
69
71
|
onConfirm={clearLocal}
|
|
70
72
|
overlayInnerStyle={{ background: lighten(0.03, theme.colorBgElevated) }}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ActionIcon, Icon } from '@lobehub/ui';
|
|
2
|
+
import { App, Dropdown, DropdownProps, MenuProps } from 'antd';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import { MoreVertical, PencilLine, Settings2, Trash } from 'lucide-react';
|
|
5
|
+
import { memo, useMemo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
import { useSessionStore } from '@/store/session';
|
|
9
|
+
|
|
10
|
+
const useStyles = createStyles(({ css }) => ({
|
|
11
|
+
modalRoot: css`
|
|
12
|
+
z-index: 2000;
|
|
13
|
+
`,
|
|
14
|
+
}));
|
|
15
|
+
interface ActionsProps extends Pick<DropdownProps, 'onOpenChange'> {
|
|
16
|
+
id?: string;
|
|
17
|
+
isCustomGroup?: boolean;
|
|
18
|
+
openConfigModal: () => void;
|
|
19
|
+
openRenameModal?: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const Actions = memo<ActionsProps>(
|
|
23
|
+
({ id, openRenameModal, openConfigModal, onOpenChange, isCustomGroup }) => {
|
|
24
|
+
const { t } = useTranslation('chat');
|
|
25
|
+
const { styles } = useStyles();
|
|
26
|
+
const { modal } = App.useApp();
|
|
27
|
+
|
|
28
|
+
const [removeSessionGroup] = useSessionStore((s) => [s.removeSessionGroup]);
|
|
29
|
+
const customGroupItems: MenuProps['items'] = useMemo(
|
|
30
|
+
() => [
|
|
31
|
+
{
|
|
32
|
+
icon: <Icon icon={PencilLine} />,
|
|
33
|
+
key: 'rename',
|
|
34
|
+
label: t('sessionGroup.rename'),
|
|
35
|
+
onClick: ({ domEvent }) => {
|
|
36
|
+
domEvent.stopPropagation();
|
|
37
|
+
openRenameModal?.();
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
icon: <Icon icon={Settings2} />,
|
|
42
|
+
key: 'config',
|
|
43
|
+
label: t('sessionGroup.config'),
|
|
44
|
+
onClick: ({ domEvent }) => {
|
|
45
|
+
domEvent.stopPropagation();
|
|
46
|
+
openConfigModal();
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: 'divider',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
danger: true,
|
|
54
|
+
icon: <Icon icon={Trash} />,
|
|
55
|
+
key: 'delete',
|
|
56
|
+
label: t('delete', { ns: 'common' }),
|
|
57
|
+
onClick: ({ domEvent }) => {
|
|
58
|
+
domEvent.stopPropagation();
|
|
59
|
+
modal.confirm({
|
|
60
|
+
centered: true,
|
|
61
|
+
okButtonProps: { danger: true },
|
|
62
|
+
onOk: () => {
|
|
63
|
+
if (!id) return;
|
|
64
|
+
removeSessionGroup(id);
|
|
65
|
+
},
|
|
66
|
+
rootClassName: styles.modalRoot,
|
|
67
|
+
title: t('sessionGroup.confirmRemoveGroupAlert'),
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
[],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const defaultItems: MenuProps['items'] = useMemo(
|
|
76
|
+
() => [
|
|
77
|
+
{
|
|
78
|
+
icon: <Icon icon={Settings2} />,
|
|
79
|
+
key: 'config',
|
|
80
|
+
label: t('sessionGroup.config'),
|
|
81
|
+
onClick: ({ domEvent }) => {
|
|
82
|
+
domEvent.stopPropagation();
|
|
83
|
+
openConfigModal();
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
[],
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<Dropdown
|
|
92
|
+
arrow={false}
|
|
93
|
+
menu={{
|
|
94
|
+
items: isCustomGroup ? customGroupItems : defaultItems,
|
|
95
|
+
onClick: ({ domEvent }) => {
|
|
96
|
+
domEvent.stopPropagation();
|
|
97
|
+
},
|
|
98
|
+
}}
|
|
99
|
+
onOpenChange={onOpenChange}
|
|
100
|
+
trigger={['click']}
|
|
101
|
+
>
|
|
102
|
+
<ActionIcon
|
|
103
|
+
icon={MoreVertical}
|
|
104
|
+
onClick={(e) => {
|
|
105
|
+
e.stopPropagation();
|
|
106
|
+
}}
|
|
107
|
+
size={{ blockSize: 22, fontSize: 16 }}
|
|
108
|
+
style={{ marginRight: -8 }}
|
|
109
|
+
/>
|
|
110
|
+
</Dropdown>
|
|
111
|
+
);
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
export default Actions;
|
|
@@ -18,8 +18,14 @@ const useStyles = createStyles(({ css, prefixCls, token, responsive }) => ({
|
|
|
18
18
|
&:hover {
|
|
19
19
|
color: ${token.colorText} !important;
|
|
20
20
|
background: ${token.colorFillTertiary};
|
|
21
|
+
.${prefixCls}-collapse-extra {
|
|
22
|
+
display: block;
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
25
|
}
|
|
26
|
+
.${prefixCls}-collapse-extra {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
23
29
|
.${prefixCls}-collapse-content {
|
|
24
30
|
border-radius: 0 !important;
|
|
25
31
|
}
|
|
@@ -1,45 +1,74 @@
|
|
|
1
1
|
import { CollapseProps } from 'antd';
|
|
2
2
|
import isEqual from 'fast-deep-equal';
|
|
3
|
-
import { memo } from 'react';
|
|
3
|
+
import { memo, useMemo, useState } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { useGlobalStore } from '@/store/global';
|
|
7
7
|
import { preferenceSelectors } from '@/store/global/selectors';
|
|
8
8
|
import { useSessionStore } from '@/store/session';
|
|
9
9
|
import { sessionSelectors } from '@/store/session/selectors';
|
|
10
|
+
import { SessionDefaultGroup } from '@/types/session';
|
|
10
11
|
|
|
12
|
+
import Actions from '../SessionListContent/CollapseGroup/Actions';
|
|
11
13
|
import CollapseGroup from './CollapseGroup';
|
|
12
14
|
import Inbox from './Inbox';
|
|
13
15
|
import SessionList from './List';
|
|
16
|
+
import ConfigGroupModal from './Modals/ConfigGroupModal';
|
|
17
|
+
import RenameGroupModal from './Modals/RenameGroupModal';
|
|
14
18
|
|
|
15
19
|
const SessionListContent = memo(() => {
|
|
16
20
|
const { t } = useTranslation('chat');
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
]);
|
|
21
|
+
|
|
22
|
+
const [activeGroupId, setActiveGroupId] = useState<string>();
|
|
23
|
+
const [renameGroupModalOpen, setRenameGroupModalOpen] = useState(false);
|
|
24
|
+
const [configGroupModalOpen, setConfigGroupModalOpen] = useState(false);
|
|
25
|
+
|
|
26
|
+
const [useFetchSessions] = useSessionStore((s) => [s.useFetchSessions]);
|
|
27
|
+
useFetchSessions();
|
|
28
|
+
|
|
29
|
+
const pinnedSessions = useSessionStore(sessionSelectors.pinnedSessions, isEqual);
|
|
30
|
+
const defaultSessions = useSessionStore(sessionSelectors.defaultSessions, isEqual);
|
|
31
|
+
const customSessionGroups = useSessionStore(sessionSelectors.customSessionGroups, isEqual);
|
|
23
32
|
|
|
24
33
|
const [sessionGroupKeys, updatePreference] = useGlobalStore((s) => [
|
|
25
34
|
preferenceSelectors.sessionGroupKeys(s),
|
|
26
35
|
s.updatePreference,
|
|
27
36
|
]);
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
const items = useMemo(
|
|
39
|
+
() =>
|
|
40
|
+
[
|
|
41
|
+
pinnedSessions.length > 0 && {
|
|
42
|
+
children: <SessionList dataSource={pinnedSessions} />,
|
|
43
|
+
extra: <Actions openConfigModal={() => setConfigGroupModalOpen(true)} />,
|
|
44
|
+
key: SessionDefaultGroup.Pinned,
|
|
45
|
+
label: t('pin'),
|
|
46
|
+
},
|
|
47
|
+
...customSessionGroups.map(({ id, name, children }) => ({
|
|
48
|
+
children: <SessionList dataSource={children} groupId={id} />,
|
|
49
|
+
extra: (
|
|
50
|
+
<Actions
|
|
51
|
+
id={id}
|
|
52
|
+
isCustomGroup
|
|
53
|
+
onOpenChange={(isOpen) => {
|
|
54
|
+
if (isOpen) setActiveGroupId(id);
|
|
55
|
+
}}
|
|
56
|
+
openConfigModal={() => setConfigGroupModalOpen(true)}
|
|
57
|
+
openRenameModal={() => setRenameGroupModalOpen(true)}
|
|
58
|
+
/>
|
|
59
|
+
),
|
|
60
|
+
key: id,
|
|
61
|
+
label: name,
|
|
62
|
+
})),
|
|
63
|
+
{
|
|
64
|
+
children: <SessionList dataSource={defaultSessions} />,
|
|
65
|
+
extra: <Actions openConfigModal={() => setConfigGroupModalOpen(true)} />,
|
|
66
|
+
key: SessionDefaultGroup.Default,
|
|
67
|
+
label: t('defaultList'),
|
|
68
|
+
},
|
|
69
|
+
].filter(Boolean) as CollapseProps['items'],
|
|
70
|
+
[customSessionGroups, pinnedSessions, defaultSessions],
|
|
71
|
+
);
|
|
43
72
|
|
|
44
73
|
return (
|
|
45
74
|
<>
|
|
@@ -48,11 +77,22 @@ const SessionListContent = memo(() => {
|
|
|
48
77
|
activeKey={sessionGroupKeys}
|
|
49
78
|
items={items}
|
|
50
79
|
onChange={(keys) => {
|
|
51
|
-
const
|
|
80
|
+
const expandSessionGroupKeys = typeof keys === 'string' ? [keys] : keys;
|
|
52
81
|
|
|
53
|
-
updatePreference({
|
|
82
|
+
updatePreference({ expandSessionGroupKeys });
|
|
54
83
|
}}
|
|
55
84
|
/>
|
|
85
|
+
{activeGroupId && (
|
|
86
|
+
<RenameGroupModal
|
|
87
|
+
id={activeGroupId}
|
|
88
|
+
onCancel={() => setRenameGroupModalOpen(false)}
|
|
89
|
+
open={renameGroupModalOpen}
|
|
90
|
+
/>
|
|
91
|
+
)}
|
|
92
|
+
<ConfigGroupModal
|
|
93
|
+
onCancel={() => setConfigGroupModalOpen(false)}
|
|
94
|
+
open={configGroupModalOpen}
|
|
95
|
+
/>
|
|
56
96
|
</>
|
|
57
97
|
);
|
|
58
98
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Icon } from '@lobehub/ui';
|
|
2
|
-
import { Button
|
|
2
|
+
import { Button } from 'antd';
|
|
3
3
|
import { Plus } from 'lucide-react';
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
@@ -7,12 +7,12 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
7
7
|
|
|
8
8
|
import { useSessionStore } from '@/store/session';
|
|
9
9
|
|
|
10
|
-
const AddButton = memo<
|
|
10
|
+
const AddButton = memo<{ groupId?: string }>(({ groupId }) => {
|
|
11
11
|
const { t } = useTranslation('chat');
|
|
12
12
|
const createSession = useSessionStore((s) => s.createSession);
|
|
13
13
|
return (
|
|
14
14
|
<Flexbox style={{ margin: '12px 16px' }}>
|
|
15
|
-
<Button block icon={<Icon icon={Plus} />} onClick={() => createSession()}
|
|
15
|
+
<Button block icon={<Icon icon={Plus} />} onClick={() => createSession({ group: groupId })}>
|
|
16
16
|
{t('newAgent')}
|
|
17
17
|
</Button>
|
|
18
18
|
</Flexbox>
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
import { ActionIcon, Icon } from '@lobehub/ui';
|
|
2
2
|
import { App, Dropdown, type MenuProps } from 'antd';
|
|
3
3
|
import { createStyles } from 'antd-style';
|
|
4
|
-
import
|
|
4
|
+
import isEqual from 'fast-deep-equal';
|
|
5
|
+
import {
|
|
6
|
+
Check,
|
|
7
|
+
HardDriveDownload,
|
|
8
|
+
ListTree,
|
|
9
|
+
LucideCopy,
|
|
10
|
+
LucidePlus,
|
|
11
|
+
MoreVertical,
|
|
12
|
+
Pin,
|
|
13
|
+
PinOff,
|
|
14
|
+
Trash,
|
|
15
|
+
} from 'lucide-react';
|
|
5
16
|
import { memo, useMemo } from 'react';
|
|
6
17
|
import { useTranslation } from 'react-i18next';
|
|
7
18
|
|
|
8
19
|
import { configService } from '@/services/config';
|
|
9
20
|
import { useSessionStore } from '@/store/session';
|
|
10
21
|
import { sessionHelpers } from '@/store/session/helpers';
|
|
11
|
-
import { sessionSelectors } from '@/store/session/selectors';
|
|
22
|
+
import { sessionGroupSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
23
|
+
import { SessionDefaultGroup } from '@/types/session';
|
|
12
24
|
|
|
13
25
|
const useStyles = createStyles(({ css }) => ({
|
|
14
26
|
modalRoot: css`
|
|
@@ -17,27 +29,35 @@ const useStyles = createStyles(({ css }) => ({
|
|
|
17
29
|
}));
|
|
18
30
|
|
|
19
31
|
interface ActionProps {
|
|
32
|
+
group: string | undefined;
|
|
20
33
|
id: string;
|
|
34
|
+
openCreateGroupModal: () => void;
|
|
21
35
|
setOpen: (open: boolean) => void;
|
|
22
36
|
}
|
|
23
37
|
|
|
24
|
-
const Actions = memo<ActionProps>(({ id, setOpen }) => {
|
|
25
|
-
const { t } = useTranslation('common');
|
|
26
|
-
|
|
38
|
+
const Actions = memo<ActionProps>(({ group, id, openCreateGroupModal, setOpen }) => {
|
|
27
39
|
const { styles } = useStyles();
|
|
40
|
+
const { t } = useTranslation('chat');
|
|
28
41
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
const sessionCustomGroups = useSessionStore(sessionGroupSelectors.sessionGroupItems, isEqual);
|
|
43
|
+
const [pin, removeSession, pinSession, duplicateSession, updateSessionGroup] = useSessionStore(
|
|
44
|
+
(s) => {
|
|
45
|
+
const session = sessionSelectors.getSessionById(id)(s);
|
|
46
|
+
return [
|
|
47
|
+
sessionHelpers.getSessionPinned(session),
|
|
48
|
+
s.removeSession,
|
|
49
|
+
s.pinSession,
|
|
50
|
+
s.duplicateSession,
|
|
51
|
+
s.updateSessionGroupId,
|
|
52
|
+
];
|
|
53
|
+
},
|
|
54
|
+
);
|
|
38
55
|
|
|
39
56
|
const { modal } = App.useApp();
|
|
40
57
|
|
|
58
|
+
const isDefault = group === SessionDefaultGroup.Default;
|
|
59
|
+
// const hasDivider = !isDefault || Object.keys(sessionByGroup).length > 0;
|
|
60
|
+
|
|
41
61
|
const items: MenuProps['items'] = useMemo(
|
|
42
62
|
() => [
|
|
43
63
|
{
|
|
@@ -48,18 +68,69 @@ const Actions = memo<ActionProps>(({ id, setOpen }) => {
|
|
|
48
68
|
pinSession(id, !pin);
|
|
49
69
|
},
|
|
50
70
|
},
|
|
71
|
+
{
|
|
72
|
+
icon: <Icon icon={LucideCopy} />,
|
|
73
|
+
key: 'duplicate',
|
|
74
|
+
label: t('duplicate', { ns: 'common' }),
|
|
75
|
+
onClick: ({ domEvent }) => {
|
|
76
|
+
domEvent.stopPropagation();
|
|
77
|
+
|
|
78
|
+
duplicateSession(id);
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: 'divider',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
children: [
|
|
86
|
+
...sessionCustomGroups.map(({ id: groupId, name }) => ({
|
|
87
|
+
icon: group === groupId ? <Icon icon={Check} /> : <div />,
|
|
88
|
+
key: groupId,
|
|
89
|
+
label: name,
|
|
90
|
+
onClick: () => {
|
|
91
|
+
updateSessionGroup(id, groupId);
|
|
92
|
+
},
|
|
93
|
+
})),
|
|
94
|
+
{
|
|
95
|
+
icon: isDefault ? <Icon icon={Check} /> : <div />,
|
|
96
|
+
key: 'defaultList',
|
|
97
|
+
label: t('defaultList'),
|
|
98
|
+
onClick: () => {
|
|
99
|
+
updateSessionGroup(id, SessionDefaultGroup.Default);
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: 'divider',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
icon: <Icon icon={LucidePlus} />,
|
|
107
|
+
key: 'createGroup',
|
|
108
|
+
label: <div>{t('sessionGroup.createGroup')}</div>,
|
|
109
|
+
onClick: ({ domEvent }) => {
|
|
110
|
+
domEvent.stopPropagation();
|
|
111
|
+
openCreateGroupModal();
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
icon: <Icon icon={ListTree} />,
|
|
116
|
+
key: 'moveGroup',
|
|
117
|
+
label: t('sessionGroup.moveGroup'),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
type: 'divider',
|
|
121
|
+
},
|
|
51
122
|
{
|
|
52
123
|
children: [
|
|
53
124
|
{
|
|
54
125
|
key: 'agent',
|
|
55
|
-
label:
|
|
126
|
+
label: t('exportType.agent', { ns: 'common' }),
|
|
56
127
|
onClick: () => {
|
|
57
128
|
configService.exportSingleAgent(id);
|
|
58
129
|
},
|
|
59
130
|
},
|
|
60
131
|
{
|
|
61
132
|
key: 'agentWithMessage',
|
|
62
|
-
label:
|
|
133
|
+
label: t('exportType.agentWithMessage', { ns: 'common' }),
|
|
63
134
|
onClick: () => {
|
|
64
135
|
configService.exportSingleSession(id);
|
|
65
136
|
},
|
|
@@ -67,32 +138,15 @@ const Actions = memo<ActionProps>(({ id, setOpen }) => {
|
|
|
67
138
|
],
|
|
68
139
|
icon: <Icon icon={HardDriveDownload} />,
|
|
69
140
|
key: 'export',
|
|
70
|
-
label: t('export'),
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
type: 'divider',
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
icon: <Icon icon={LucideCopy} />,
|
|
77
|
-
key: 'duplicate',
|
|
78
|
-
label: t('duplicate'),
|
|
79
|
-
onClick: ({ domEvent }) => {
|
|
80
|
-
domEvent.stopPropagation();
|
|
81
|
-
|
|
82
|
-
duplicateSession(id);
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
type: 'divider',
|
|
141
|
+
label: t('export', { ns: 'common' }),
|
|
87
142
|
},
|
|
88
143
|
{
|
|
89
144
|
danger: true,
|
|
90
145
|
icon: <Icon icon={Trash} />,
|
|
91
146
|
key: 'delete',
|
|
92
|
-
label: t('delete'),
|
|
147
|
+
label: t('delete', { ns: 'common' }),
|
|
93
148
|
onClick: ({ domEvent }) => {
|
|
94
149
|
domEvent.stopPropagation();
|
|
95
|
-
|
|
96
150
|
modal.confirm({
|
|
97
151
|
centered: true,
|
|
98
152
|
okButtonProps: { danger: true },
|