@lobehub/chat 0.44.0 → 0.44.1

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 CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.44.1](https://github.com/lobehub/lobe-chat/compare/v0.44.0...v0.44.1)
6
+
7
+ <sup>Released on **2023-08-12**</sup>
8
+
9
+ <br/>
10
+
11
+ <details>
12
+ <summary><kbd>Improvements and Fixes</kbd></summary>
13
+
14
+ </details>
15
+
16
+ <div align="right">
17
+
18
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
19
+
20
+ </div>
21
+
5
22
  ## [Version 0.44.0](https://github.com/lobehub/lobe-chat/compare/v0.43.0...v0.44.0)
6
23
 
7
24
  <sup>Released on **2023-08-12**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.44.0",
3
+ "version": "0.44.1",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -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 }: PropsWithChildren) => {
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, switchInbox] = useSessionStore((s) => [s.activeId, s.switchInbox]);
12
+ const [activeId] = useSessionStore((s) => [s.activeId]);
12
13
 
13
14
  return (
14
- <List.Item
15
- active={activeId === INBOX_SESSION_ID}
16
- avatar={<Avatar avatar={DEFAULT_INBOX_AVATAR} size={46} style={{ padding: 3 }} />}
17
- onClick={switchInbox}
18
- title={t('inbox.title')}
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 SkeletonItem = () => {
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 SkeletonItem;
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 './SessionItem';
9
- import SkeletonItem from './SkeletonItem';
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
- <SkeletonItem />
17
+ <SkeletonList />
19
18
  ) : (
20
19
  <Flexbox>
21
20
  {list.map(({ id }) => (
22
21
  <Link href={`/chat/${id}`} key={id}>
23
- <SessionItem active={activeId === id} id={id} loading={loading && id === activeId} />
22
+ <SessionItem id={id} />
24
23
  </Link>
25
24
  ))}
26
25
  </Flexbox>
@@ -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;