@lobehub/chat 0.44.0 → 0.44.2
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/CHANGELOG.md +42 -0
- package/package.json +1 -1
- package/src/layout/index.tsx +5 -5
- package/src/pages/chat/SessionList/Inbox.tsx +9 -7
- package/src/pages/chat/SessionList/List/Item/Actions.tsx +112 -0
- package/src/pages/chat/SessionList/List/Item/index.tsx +115 -0
- package/src/pages/chat/SessionList/List/{SkeletonItem.tsx → SkeletonList.tsx} +2 -2
- package/src/pages/chat/SessionList/List/index.tsx +4 -5
- package/src/store/session/slices/chat/actions/message.ts +21 -9
- package/src/store/session/slices/session/action.ts +3 -9
- package/src/pages/chat/SessionList/List/SessionItem.tsx +0 -189
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,48 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.44.2](https://github.com/lobehub/lobe-chat/compare/v0.44.1...v0.44.2)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-08-13**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: 修正重新生成时切分历史消息的逻辑.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: 修正重新生成时切分历史消息的逻辑, closes [#50](https://github.com/lobehub/lobe-chat/issues/50) ([de5141f](https://github.com/lobehub/lobe-chat/commit/de5141f))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.44.1](https://github.com/lobehub/lobe-chat/compare/v0.44.0...v0.44.1)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2023-08-12**</sup>
|
|
33
|
+
|
|
34
|
+
<br/>
|
|
35
|
+
|
|
36
|
+
<details>
|
|
37
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
38
|
+
|
|
39
|
+
</details>
|
|
40
|
+
|
|
41
|
+
<div align="right">
|
|
42
|
+
|
|
43
|
+
[](#readme-top)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
5
47
|
## [Version 0.44.0](https://github.com/lobehub/lobe-chat/compare/v0.43.0...v0.44.0)
|
|
6
48
|
|
|
7
49
|
<sup>Released on **2023-08-12**</sup>
|
package/package.json
CHANGED
package/src/layout/index.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import { App, ConfigProvider } from 'antd';
|
|
|
3
3
|
import { useThemeMode } from 'antd-style';
|
|
4
4
|
import 'antd/dist/reset.css';
|
|
5
5
|
import Zh_CN from 'antd/locale/zh_CN';
|
|
6
|
-
import { PropsWithChildren, useCallback, useEffect } from 'react';
|
|
6
|
+
import { PropsWithChildren, memo, useCallback, useEffect } from 'react';
|
|
7
7
|
|
|
8
8
|
import { useGlobalStore } from '@/store/global';
|
|
9
9
|
import { useSessionStore } from '@/store/session';
|
|
@@ -12,7 +12,7 @@ import { GlobalStyle } from '@/styles';
|
|
|
12
12
|
import i18n from '../locales';
|
|
13
13
|
import { useStyles } from './style';
|
|
14
14
|
|
|
15
|
-
const Layout = ({ children }
|
|
15
|
+
const Layout = memo<PropsWithChildren>(({ children }) => {
|
|
16
16
|
const { styles } = useStyles();
|
|
17
17
|
|
|
18
18
|
useEffect(() => {
|
|
@@ -25,9 +25,9 @@ const Layout = ({ children }: PropsWithChildren) => {
|
|
|
25
25
|
<App className={styles.bg}>{children}</App>
|
|
26
26
|
</ConfigProvider>
|
|
27
27
|
);
|
|
28
|
-
};
|
|
28
|
+
});
|
|
29
29
|
|
|
30
|
-
export default ({ children }: PropsWithChildren) => {
|
|
30
|
+
export default memo(({ children }: PropsWithChildren) => {
|
|
31
31
|
const themeMode = useGlobalStore((s) => s.settings.themeMode);
|
|
32
32
|
const [primaryColor, neutralColor] = useGlobalStore((s) => [
|
|
33
33
|
s.settings.primaryColor,
|
|
@@ -54,4 +54,4 @@ export default ({ children }: PropsWithChildren) => {
|
|
|
54
54
|
<Layout>{children}</Layout>
|
|
55
55
|
</ThemeProvider>
|
|
56
56
|
);
|
|
57
|
-
};
|
|
57
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Avatar, List } from '@lobehub/ui';
|
|
2
|
+
import Link from 'next/link';
|
|
2
3
|
import { memo } from 'react';
|
|
3
4
|
import { useTranslation } from 'react-i18next';
|
|
4
5
|
|
|
@@ -8,15 +9,16 @@ import { useSessionStore } from '@/store/session';
|
|
|
8
9
|
|
|
9
10
|
const Inbox = memo(() => {
|
|
10
11
|
const { t } = useTranslation('common');
|
|
11
|
-
const [activeId
|
|
12
|
+
const [activeId] = useSessionStore((s) => [s.activeId]);
|
|
12
13
|
|
|
13
14
|
return (
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
<Link href={'/chat'}>
|
|
16
|
+
<List.Item
|
|
17
|
+
active={activeId === INBOX_SESSION_ID}
|
|
18
|
+
avatar={<Avatar avatar={DEFAULT_INBOX_AVATAR} size={46} style={{ padding: 3 }} />}
|
|
19
|
+
title={t('inbox.title')}
|
|
20
|
+
/>
|
|
21
|
+
</Link>
|
|
20
22
|
);
|
|
21
23
|
});
|
|
22
24
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ActionIcon, Icon } from '@lobehub/ui';
|
|
2
|
+
import { App, Dropdown, type MenuProps } from 'antd';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import { FolderOutput, MoreVertical, Pin, PinOff, Trash } from 'lucide-react';
|
|
5
|
+
import { memo, useMemo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
|
9
|
+
import { sessionSelectors, useSessionStore } from '@/store/session';
|
|
10
|
+
|
|
11
|
+
const useStyles = createStyles(({ css }) => ({
|
|
12
|
+
modalRoot: css`
|
|
13
|
+
z-index: 2000;
|
|
14
|
+
`,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
interface ActionProps {
|
|
18
|
+
id: string;
|
|
19
|
+
setOpen: (open: boolean) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const Actions = memo<ActionProps>(({ id, setOpen }) => {
|
|
23
|
+
const { t } = useTranslation('common');
|
|
24
|
+
|
|
25
|
+
const { styles } = useStyles();
|
|
26
|
+
|
|
27
|
+
const [pin, removeSession, pinSession] = useSessionStore((s) => {
|
|
28
|
+
const session = sessionSelectors.getSessionById(id)(s);
|
|
29
|
+
return [session.pinned, s.removeSession, s.pinSession];
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const { modal } = App.useApp();
|
|
33
|
+
|
|
34
|
+
const items: MenuProps['items'] = useMemo(
|
|
35
|
+
() => [
|
|
36
|
+
{
|
|
37
|
+
icon: <Icon icon={pin ? PinOff : Pin} />,
|
|
38
|
+
key: 'pin',
|
|
39
|
+
label: t(pin ? 'pinOff' : 'pin'),
|
|
40
|
+
onClick: () => {
|
|
41
|
+
pinSession(id, !pin);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
children: [
|
|
46
|
+
{
|
|
47
|
+
key: 'agent',
|
|
48
|
+
label: <div>{t('exportType.agent')}</div>,
|
|
49
|
+
onClick: () => {
|
|
50
|
+
exportSingleAgent(id);
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: 'agentWithMessage',
|
|
55
|
+
label: <div>{t('exportType.agentWithMessage')}</div>,
|
|
56
|
+
onClick: () => {
|
|
57
|
+
exportSingleSession(id);
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
icon: <Icon icon={FolderOutput} />,
|
|
62
|
+
key: 'export',
|
|
63
|
+
label: t('export'),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
danger: true,
|
|
67
|
+
icon: <Icon icon={Trash} />,
|
|
68
|
+
key: 'delete',
|
|
69
|
+
label: t('delete'),
|
|
70
|
+
onClick: ({ domEvent }) => {
|
|
71
|
+
domEvent.stopPropagation();
|
|
72
|
+
|
|
73
|
+
modal.confirm({
|
|
74
|
+
centered: true,
|
|
75
|
+
okButtonProps: { danger: true },
|
|
76
|
+
okText: t('ok'),
|
|
77
|
+
onOk: () => {
|
|
78
|
+
removeSession(id);
|
|
79
|
+
},
|
|
80
|
+
rootClassName: styles.modalRoot,
|
|
81
|
+
title: t('confirmRemoveSessionItemAlert'),
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
[id, pin],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Dropdown
|
|
91
|
+
arrow={false}
|
|
92
|
+
menu={{
|
|
93
|
+
items,
|
|
94
|
+
onClick: ({ domEvent }) => {
|
|
95
|
+
domEvent.stopPropagation();
|
|
96
|
+
},
|
|
97
|
+
}}
|
|
98
|
+
onOpenChange={setOpen}
|
|
99
|
+
trigger={['click']}
|
|
100
|
+
>
|
|
101
|
+
<ActionIcon
|
|
102
|
+
icon={MoreVertical}
|
|
103
|
+
size={{
|
|
104
|
+
blockSize: 28,
|
|
105
|
+
fontSize: 16,
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
</Dropdown>
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
export default Actions;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Avatar, List } from '@lobehub/ui';
|
|
2
|
+
import { useHover } from 'ahooks';
|
|
3
|
+
import { Tag } from 'antd';
|
|
4
|
+
import { createStyles } from 'antd-style';
|
|
5
|
+
import { memo, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
import { shallow } from 'zustand/shallow';
|
|
8
|
+
|
|
9
|
+
import { useGlobalStore } from '@/store/global';
|
|
10
|
+
import { agentSelectors, sessionSelectors, useSessionStore } from '@/store/session';
|
|
11
|
+
|
|
12
|
+
import Actions from './Actions';
|
|
13
|
+
|
|
14
|
+
const { Item } = List;
|
|
15
|
+
|
|
16
|
+
const useStyles = createStyles(({ css }) => {
|
|
17
|
+
return {
|
|
18
|
+
container: css`
|
|
19
|
+
position: relative;
|
|
20
|
+
`,
|
|
21
|
+
|
|
22
|
+
modalRoot: css`
|
|
23
|
+
z-index: 2000;
|
|
24
|
+
`,
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
interface SessionItemProps {
|
|
29
|
+
id: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SessionItem = memo<SessionItemProps>(({ id }) => {
|
|
33
|
+
const [open, setOpen] = useState(false);
|
|
34
|
+
const ref = useRef(null);
|
|
35
|
+
const isHovering = useHover(ref);
|
|
36
|
+
|
|
37
|
+
const { styles } = useStyles();
|
|
38
|
+
const [defaultModel] = useGlobalStore((s) => [s.settings.defaultAgent.config?.model]);
|
|
39
|
+
|
|
40
|
+
const [
|
|
41
|
+
active,
|
|
42
|
+
loading,
|
|
43
|
+
pin,
|
|
44
|
+
title,
|
|
45
|
+
description,
|
|
46
|
+
systemRole,
|
|
47
|
+
avatar,
|
|
48
|
+
avatarBackground,
|
|
49
|
+
updateAt,
|
|
50
|
+
model,
|
|
51
|
+
] = useSessionStore((s) => {
|
|
52
|
+
const session = sessionSelectors.getSessionById(id)(s);
|
|
53
|
+
const meta = session.meta;
|
|
54
|
+
const systemRole = session.config.systemRole;
|
|
55
|
+
|
|
56
|
+
return [
|
|
57
|
+
s.activeId === id,
|
|
58
|
+
s.autocompleteLoading.title && id === s.activeId,
|
|
59
|
+
session.pinned,
|
|
60
|
+
agentSelectors.getTitle(meta),
|
|
61
|
+
agentSelectors.getDescription(meta),
|
|
62
|
+
systemRole,
|
|
63
|
+
agentSelectors.getAvatar(meta),
|
|
64
|
+
meta.backgroundColor,
|
|
65
|
+
session?.updateAt,
|
|
66
|
+
session.config.model,
|
|
67
|
+
];
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const showModel = model !== defaultModel;
|
|
71
|
+
|
|
72
|
+
const actions = useMemo(() => <Actions id={id} setOpen={setOpen} />, [id]);
|
|
73
|
+
|
|
74
|
+
const avatarRender = useMemo(
|
|
75
|
+
() => (
|
|
76
|
+
<Avatar
|
|
77
|
+
animation={isHovering}
|
|
78
|
+
avatar={avatar}
|
|
79
|
+
background={avatarBackground}
|
|
80
|
+
shape="circle"
|
|
81
|
+
size={46}
|
|
82
|
+
/>
|
|
83
|
+
),
|
|
84
|
+
[isHovering, avatar, avatarBackground],
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const addon = useMemo(
|
|
88
|
+
() =>
|
|
89
|
+
!showModel ? undefined : (
|
|
90
|
+
<Flexbox gap={4} horizontal style={{ flexWrap: 'wrap' }}>
|
|
91
|
+
{showModel && <Tag bordered={false}>{model}</Tag>}
|
|
92
|
+
</Flexbox>
|
|
93
|
+
),
|
|
94
|
+
[showModel, model],
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<Item
|
|
99
|
+
actions={actions}
|
|
100
|
+
active={active}
|
|
101
|
+
addon={addon}
|
|
102
|
+
avatar={avatarRender}
|
|
103
|
+
className={styles.container}
|
|
104
|
+
date={updateAt}
|
|
105
|
+
description={description || systemRole}
|
|
106
|
+
loading={loading}
|
|
107
|
+
pin={pin}
|
|
108
|
+
ref={ref}
|
|
109
|
+
showAction={open || isHovering}
|
|
110
|
+
title={title}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
113
|
+
}, shallow);
|
|
114
|
+
|
|
115
|
+
export default SessionItem;
|
|
@@ -25,7 +25,7 @@ const useStyles = createStyles(({ css }) => ({
|
|
|
25
25
|
|
|
26
26
|
// 从 3~10 随机取一个整数
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const SkeletonList = () => {
|
|
29
29
|
const { styles } = useStyles();
|
|
30
30
|
|
|
31
31
|
const list = Array.from({ length: 4 }).fill('');
|
|
@@ -44,4 +44,4 @@ const SkeletonItem = () => {
|
|
|
44
44
|
</Flexbox>
|
|
45
45
|
);
|
|
46
46
|
};
|
|
47
|
-
export default
|
|
47
|
+
export default SkeletonList;
|
|
@@ -5,22 +5,21 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
5
5
|
|
|
6
6
|
import { sessionSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
|
|
7
7
|
|
|
8
|
-
import SessionItem from './
|
|
9
|
-
import
|
|
8
|
+
import SessionItem from './Item';
|
|
9
|
+
import SkeletonList from './SkeletonList';
|
|
10
10
|
|
|
11
11
|
const SessionList = memo(() => {
|
|
12
12
|
const list = useSessionStore(sessionSelectors.sessionList, isEqual);
|
|
13
|
-
const [activeId, loading] = useSessionStore((s) => [s.activeId, s.autocompleteLoading.title]);
|
|
14
13
|
|
|
15
14
|
const isInit = useSessionHydrated();
|
|
16
15
|
|
|
17
16
|
return !isInit ? (
|
|
18
|
-
<
|
|
17
|
+
<SkeletonList />
|
|
19
18
|
) : (
|
|
20
19
|
<Flexbox>
|
|
21
20
|
{list.map(({ id }) => (
|
|
22
21
|
<Link href={`/chat/${id}`} key={id}>
|
|
23
|
-
<SessionItem
|
|
22
|
+
<SessionItem id={id} />
|
|
24
23
|
</Link>
|
|
25
24
|
))}
|
|
26
25
|
</Flexbox>
|
|
@@ -223,22 +223,34 @@ export const chatMessage: StateCreator<
|
|
|
223
223
|
const chats = chatSelectors.currentChats(get());
|
|
224
224
|
|
|
225
225
|
const currentIndex = chats.findIndex((c) => c.id === messageId);
|
|
226
|
+
const currentMessage = chats[currentIndex];
|
|
227
|
+
let contextMessages: ChatMessage[] = [];
|
|
228
|
+
|
|
229
|
+
switch (currentMessage.role) {
|
|
230
|
+
case 'function':
|
|
231
|
+
case 'user': {
|
|
232
|
+
contextMessages = chats.slice(0, currentIndex + 1);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
case 'assistant': {
|
|
236
|
+
// 消息是 AI 发出的因此需要找到它的 user 消息
|
|
237
|
+
const userId = currentMessage.parentId;
|
|
238
|
+
const userIndex = chats.findIndex((c) => c.id === userId);
|
|
239
|
+
// 如果消息没有 parentId,那么同 user/function 模式
|
|
240
|
+
contextMessages = chats.slice(0, userIndex < 0 ? currentIndex + 1 : userIndex + 1);
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
226
244
|
|
|
227
|
-
|
|
228
|
-
.slice(0, currentIndex + 1)
|
|
229
|
-
// 如果点击重新发送的 message 其 role 是 assistant 或者 function,那么需要移除
|
|
230
|
-
// 如果点击重新发送的 message 其 role 是 user,则不需要移除
|
|
231
|
-
.filter((c) => !(c.role === 'assistant' && c.id === messageId));
|
|
232
|
-
|
|
233
|
-
if (histories.length <= 0) return;
|
|
245
|
+
if (contextMessages.length <= 0) return;
|
|
234
246
|
|
|
235
247
|
const { realFetchAIResponse } = get();
|
|
236
248
|
|
|
237
|
-
const latestMsg =
|
|
249
|
+
const latestMsg = contextMessages.filter((s) => s.role === 'user').at(-1);
|
|
238
250
|
|
|
239
251
|
if (!latestMsg) return;
|
|
240
252
|
|
|
241
|
-
await realFetchAIResponse(
|
|
253
|
+
await realFetchAIResponse(contextMessages, latestMsg.id);
|
|
242
254
|
},
|
|
243
255
|
|
|
244
256
|
sendMessage: async (message) => {
|
|
@@ -29,6 +29,7 @@ export interface SessionAction {
|
|
|
29
29
|
* @param payload - 聊天记录
|
|
30
30
|
*/
|
|
31
31
|
dispatchSession: (payload: SessionDispatch) => void;
|
|
32
|
+
importInbox: (inbox: LobeAgentSession) => void;
|
|
32
33
|
/**
|
|
33
34
|
* 导入会话
|
|
34
35
|
* @param sessions
|
|
@@ -52,7 +53,6 @@ export interface SessionAction {
|
|
|
52
53
|
* @returns void
|
|
53
54
|
*/
|
|
54
55
|
removeSession: (sessionId: string) => void;
|
|
55
|
-
switchInbox: () => void;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* @title 切换会话
|
|
@@ -114,7 +114,8 @@ export const createSessionSlice: StateCreator<
|
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
116
|
},
|
|
117
|
-
|
|
117
|
+
// TODO:暂时先不实现导入 inbox 的功能
|
|
118
|
+
importInbox: () => {},
|
|
118
119
|
importSessions: (importSessions) => {
|
|
119
120
|
const { sessions } = get();
|
|
120
121
|
set(
|
|
@@ -147,13 +148,6 @@ export const createSessionSlice: StateCreator<
|
|
|
147
148
|
}
|
|
148
149
|
},
|
|
149
150
|
|
|
150
|
-
switchInbox: () => {
|
|
151
|
-
if (get().activeId === INBOX_SESSION_ID) return;
|
|
152
|
-
get().activeSession(INBOX_SESSION_ID);
|
|
153
|
-
|
|
154
|
-
Router.push('/chat');
|
|
155
|
-
},
|
|
156
|
-
|
|
157
151
|
switchSession: async (sessionId) => {
|
|
158
152
|
if (get().activeId === sessionId) return;
|
|
159
153
|
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { ActionIcon, Avatar, Icon, List } from '@lobehub/ui';
|
|
2
|
-
import { useHover } from 'ahooks';
|
|
3
|
-
import { App, Dropdown, type MenuProps, Tag } from 'antd';
|
|
4
|
-
import { createStyles } from 'antd-style';
|
|
5
|
-
import { FolderOutput, MoreVertical, Pin, PinOff, Trash } from 'lucide-react';
|
|
6
|
-
import { memo, useMemo, useRef, useState } from 'react';
|
|
7
|
-
import { useTranslation } from 'react-i18next';
|
|
8
|
-
import { Flexbox } from 'react-layout-kit';
|
|
9
|
-
import { shallow } from 'zustand/shallow';
|
|
10
|
-
|
|
11
|
-
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
|
12
|
-
import { useGlobalStore } from '@/store/global';
|
|
13
|
-
import { agentSelectors, chatSelectors, sessionSelectors, useSessionStore } from '@/store/session';
|
|
14
|
-
|
|
15
|
-
const { Item } = List;
|
|
16
|
-
|
|
17
|
-
const useStyles = createStyles(({ css }) => {
|
|
18
|
-
return {
|
|
19
|
-
container: css`
|
|
20
|
-
position: relative;
|
|
21
|
-
`,
|
|
22
|
-
|
|
23
|
-
modalRoot: css`
|
|
24
|
-
z-index: 2000;
|
|
25
|
-
`,
|
|
26
|
-
};
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
interface SessionItemProps {
|
|
30
|
-
active?: boolean;
|
|
31
|
-
id: string;
|
|
32
|
-
loading?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const SessionItem = memo<SessionItemProps>(({ id, active = true, loading }) => {
|
|
36
|
-
const [open, setOpen] = useState(false);
|
|
37
|
-
const ref = useRef(null);
|
|
38
|
-
const isHovering = useHover(ref);
|
|
39
|
-
|
|
40
|
-
const { t } = useTranslation('common');
|
|
41
|
-
|
|
42
|
-
const { styles } = useStyles();
|
|
43
|
-
const [defaultModel] = useGlobalStore((s) => [s.settings.defaultAgent.config?.model]);
|
|
44
|
-
|
|
45
|
-
const [
|
|
46
|
-
pin,
|
|
47
|
-
title,
|
|
48
|
-
description,
|
|
49
|
-
systemRole,
|
|
50
|
-
avatar,
|
|
51
|
-
avatarBackground,
|
|
52
|
-
updateAt,
|
|
53
|
-
model,
|
|
54
|
-
chatLength,
|
|
55
|
-
removeSession,
|
|
56
|
-
pinSession,
|
|
57
|
-
] = useSessionStore((s) => {
|
|
58
|
-
const session = sessionSelectors.getSessionById(id)(s);
|
|
59
|
-
const meta = session.meta;
|
|
60
|
-
const systemRole = session.config.systemRole;
|
|
61
|
-
return [
|
|
62
|
-
session.pinned,
|
|
63
|
-
agentSelectors.getTitle(meta),
|
|
64
|
-
agentSelectors.getDescription(meta),
|
|
65
|
-
systemRole,
|
|
66
|
-
agentSelectors.getAvatar(meta),
|
|
67
|
-
meta.backgroundColor,
|
|
68
|
-
session?.updateAt,
|
|
69
|
-
session.config.model,
|
|
70
|
-
chatSelectors.getChatsById(id)(s).length,
|
|
71
|
-
s.removeSession,
|
|
72
|
-
s.pinSession,
|
|
73
|
-
];
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const { modal } = App.useApp();
|
|
77
|
-
const items: MenuProps['items'] = useMemo(
|
|
78
|
-
() => [
|
|
79
|
-
{
|
|
80
|
-
icon: <Icon icon={pin ? PinOff : Pin} />,
|
|
81
|
-
key: 'pin',
|
|
82
|
-
label: t(pin ? 'pinOff' : 'pin'),
|
|
83
|
-
onClick: () => {
|
|
84
|
-
pinSession(id, !pin);
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
children: [
|
|
89
|
-
{
|
|
90
|
-
key: 'agent',
|
|
91
|
-
label: <div>{t('exportType.agent')}</div>,
|
|
92
|
-
onClick: () => {
|
|
93
|
-
exportSingleAgent(id);
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
key: 'agentWithMessage',
|
|
98
|
-
label: <div>{t('exportType.agentWithMessage')}</div>,
|
|
99
|
-
onClick: () => {
|
|
100
|
-
exportSingleSession(id);
|
|
101
|
-
},
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
icon: <Icon icon={FolderOutput} />,
|
|
105
|
-
key: 'export',
|
|
106
|
-
label: t('export'),
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
danger: true,
|
|
110
|
-
icon: <Icon icon={Trash} />,
|
|
111
|
-
key: 'delete',
|
|
112
|
-
label: t('delete'),
|
|
113
|
-
onClick: ({ domEvent }) => {
|
|
114
|
-
domEvent.stopPropagation();
|
|
115
|
-
|
|
116
|
-
modal.confirm({
|
|
117
|
-
centered: true,
|
|
118
|
-
okButtonProps: { danger: true },
|
|
119
|
-
okText: t('ok'),
|
|
120
|
-
onOk: () => {
|
|
121
|
-
removeSession(id);
|
|
122
|
-
},
|
|
123
|
-
rootClassName: styles.modalRoot,
|
|
124
|
-
title: t('confirmRemoveSessionItemAlert'),
|
|
125
|
-
});
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
[id, pin],
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
const showModel = model !== defaultModel;
|
|
133
|
-
const showChatLength = chatLength > 0;
|
|
134
|
-
|
|
135
|
-
return (
|
|
136
|
-
<Item
|
|
137
|
-
actions={
|
|
138
|
-
<Dropdown
|
|
139
|
-
arrow={false}
|
|
140
|
-
menu={{
|
|
141
|
-
items,
|
|
142
|
-
onClick: ({ domEvent }) => {
|
|
143
|
-
domEvent.stopPropagation();
|
|
144
|
-
},
|
|
145
|
-
}}
|
|
146
|
-
onOpenChange={setOpen}
|
|
147
|
-
open={open}
|
|
148
|
-
trigger={['click']}
|
|
149
|
-
>
|
|
150
|
-
<ActionIcon
|
|
151
|
-
icon={MoreVertical}
|
|
152
|
-
size={{
|
|
153
|
-
blockSize: 28,
|
|
154
|
-
fontSize: 16,
|
|
155
|
-
}}
|
|
156
|
-
/>
|
|
157
|
-
</Dropdown>
|
|
158
|
-
}
|
|
159
|
-
active={active}
|
|
160
|
-
addon={
|
|
161
|
-
!(showModel || showChatLength) ? undefined : (
|
|
162
|
-
<Flexbox gap={4} horizontal style={{ flexWrap: 'wrap' }}>
|
|
163
|
-
{showModel && <Tag bordered={false}>{model}</Tag>}
|
|
164
|
-
</Flexbox>
|
|
165
|
-
)
|
|
166
|
-
}
|
|
167
|
-
avatar={
|
|
168
|
-
<Avatar
|
|
169
|
-
animation={isHovering}
|
|
170
|
-
avatar={avatar}
|
|
171
|
-
background={avatarBackground}
|
|
172
|
-
shape="circle"
|
|
173
|
-
size={46}
|
|
174
|
-
title={title}
|
|
175
|
-
/>
|
|
176
|
-
}
|
|
177
|
-
className={styles.container}
|
|
178
|
-
date={updateAt}
|
|
179
|
-
description={description || systemRole}
|
|
180
|
-
loading={loading}
|
|
181
|
-
pin={pin}
|
|
182
|
-
ref={ref}
|
|
183
|
-
showAction={open || isHovering}
|
|
184
|
-
title={title}
|
|
185
|
-
/>
|
|
186
|
-
);
|
|
187
|
-
}, shallow);
|
|
188
|
-
|
|
189
|
-
export default SessionItem;
|