@lobehub/chat 0.43.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.
Files changed (76) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/CHANGELOG.md +66 -0
  3. package/package.json +5 -2
  4. package/src/const/meta.ts +3 -0
  5. package/src/const/session.ts +1 -0
  6. package/src/helpers/export.ts +15 -9
  7. package/src/layout/index.tsx +5 -5
  8. package/src/locales/default/common.ts +8 -1
  9. package/src/pages/chat/SessionList/Inbox.tsx +25 -0
  10. package/src/pages/chat/SessionList/List/Item/Actions.tsx +112 -0
  11. package/src/pages/chat/SessionList/List/Item/index.tsx +115 -0
  12. package/src/pages/chat/SessionList/List/{SkeletonItem.tsx → SkeletonList.tsx} +2 -2
  13. package/src/pages/chat/SessionList/List/index.tsx +7 -7
  14. package/src/pages/chat/SessionList/index.tsx +13 -1
  15. package/src/pages/chat/[id]/index.page.tsx +7 -9
  16. package/src/pages/chat/[id]/layout.tsx +7 -23
  17. package/src/pages/chat/[id]/setting/index.page.tsx +3 -3
  18. package/src/pages/chat/features/Conversation/ChatList/SkeletonList.tsx +43 -0
  19. package/src/pages/chat/{[id] → features}/Conversation/ChatList/index.tsx +35 -5
  20. package/src/pages/chat/{[id] → features}/Conversation/Input/index.tsx +1 -1
  21. package/src/pages/chat/{[id] → features}/Header/index.tsx +32 -34
  22. package/src/pages/chat/features/Sidebar/Inner.tsx +42 -0
  23. package/src/pages/chat/{[id]/Sidebar/Inner.tsx → features/Sidebar/SystemRole.tsx} +7 -37
  24. package/src/pages/chat/{[id] → features}/Sidebar/index.tsx +8 -4
  25. package/src/pages/chat/index.page.tsx +39 -1
  26. package/src/pages/chat/layout.tsx +27 -0
  27. package/src/pages/welcome/Footer/index.tsx +1 -0
  28. package/src/services/chatModel.ts +1 -1
  29. package/src/store/global/hooks/useOnFinishHydrationGlobal.ts +18 -0
  30. package/src/store/global/hooks/useSwitchSideBarOnInit.ts +4 -8
  31. package/src/store/session/hooks/index.ts +2 -0
  32. package/src/store/session/hooks/useOnFinishHydrationSession.ts +18 -0
  33. package/src/store/session/hooks/useSessionHydrated.ts +1 -1
  34. package/src/store/session/index.ts +1 -3
  35. package/src/store/session/initialState.ts +6 -3
  36. package/src/store/session/selectors.ts +3 -3
  37. package/src/store/session/slices/agentConfig/action.ts +2 -1
  38. package/src/store/session/slices/agentConfig/selectors.ts +11 -11
  39. package/src/store/session/slices/chat/actions/message.ts +13 -20
  40. package/src/store/session/slices/chat/actions/topic.ts +3 -1
  41. package/src/store/session/slices/chat/selectors/chat.ts +1 -1
  42. package/src/store/session/slices/chat/selectors/token.ts +1 -1
  43. package/src/store/session/slices/chat/selectors/topic.ts +4 -2
  44. package/src/store/session/slices/session/action.ts +24 -16
  45. package/src/store/session/slices/session/initialState.ts +18 -4
  46. package/src/store/session/slices/session/selectors/export.ts +13 -8
  47. package/src/store/session/slices/session/selectors/list.test.ts +303 -0
  48. package/src/store/session/slices/session/selectors/list.ts +8 -3
  49. package/src/store/session/store.ts +5 -5
  50. package/src/types/exportConfig.ts +93 -31
  51. package/src/utils/config.ts +9 -0
  52. package/src/pages/chat/SessionList/List/SessionItem.tsx +0 -178
  53. package/src/pages/chat/SessionList/List/style.ts +0 -36
  54. package/src/store/session/slices/chat/index.ts +0 -3
  55. package/src/store/session/slices/session/index.ts +0 -3
  56. package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/ApiKeyForm.tsx +0 -0
  57. package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/ErrorActionContainer.tsx +0 -0
  58. package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/InvalidAccess.tsx +1 -1
  59. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/OpenAiBizError.tsx +0 -0
  60. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Error/style.tsx +0 -0
  61. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/MessageExtra.tsx +0 -0
  62. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/OTPInput.tsx +0 -0
  63. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/FunctionCall.tsx +0 -0
  64. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/PluginMessage.tsx +0 -0
  65. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/PluginResultRender.tsx +0 -0
  66. /package/src/pages/chat/{[id] → features}/Conversation/ChatList/Plugins/style.ts +0 -0
  67. /package/src/pages/chat/{[id] → features}/Conversation/Input/Action.tsx +0 -0
  68. /package/src/pages/chat/{[id] → features}/Conversation/Input/ActionRight.tsx +0 -0
  69. /package/src/pages/chat/{[id] → features}/Conversation/Input/Token.tsx +0 -0
  70. /package/src/pages/chat/{[id] → features}/Conversation/index.tsx +0 -0
  71. /package/src/pages/chat/{[id] → features}/Header/PluginTag.tsx +0 -0
  72. /package/src/pages/chat/{[id] → features}/Sidebar/Header.tsx +0 -0
  73. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/DefaultContent.tsx +0 -0
  74. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/TopicContent.tsx +0 -0
  75. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/TopicItem.tsx +0 -0
  76. /package/src/pages/chat/{[id] → features}/Sidebar/Topic/index.tsx +0 -0
@@ -25,7 +25,7 @@ jobs:
25
25
  run: pnpm install
26
26
 
27
27
  - name: Lint
28
- run: pnpm run type-check
28
+ run: pnpm run lint
29
29
 
30
30
  - name: Test
31
31
  run: pnpm run test
package/CHANGELOG.md CHANGED
@@ -2,6 +2,72 @@
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
+
22
+ ## [Version 0.44.0](https://github.com/lobehub/lobe-chat/compare/v0.43.0...v0.44.0)
23
+
24
+ <sup>Released on **2023-08-12**</sup>
25
+
26
+ #### ♻ Code Refactoring
27
+
28
+ - **misc**: 优化 Inbox 会话的实现逻辑, 将 chat 中的功能模型拆分到 features 中, 重构 session 相关实现,移除循环依赖.
29
+
30
+ #### ✨ Features
31
+
32
+ - **misc**: 支持 inbox 消息导出, 支持 inbox 的会话功能, 新增 inbox 数据模型, 新增 inbox 模块入口.
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Fix Inbox defaultMessage avatar, 优化 header 的 setting 展示, 优化门禁下默认的解锁方式, 补充 ChatList 的 Loading 态.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Code refactoring
44
+
45
+ - **misc**: 优化 Inbox 会话的实现逻辑 ([22cc4cf](https://github.com/lobehub/lobe-chat/commit/22cc4cf))
46
+ - **misc**: 将 chat 中的功能模型拆分到 features 中 ([e25a856](https://github.com/lobehub/lobe-chat/commit/e25a856))
47
+ - **misc**: 重构 session 相关实现,移除循环依赖 ([9acf65c](https://github.com/lobehub/lobe-chat/commit/9acf65c))
48
+
49
+ #### What's improved
50
+
51
+ - **misc**: 支持 inbox 消息导出 ([498e075](https://github.com/lobehub/lobe-chat/commit/498e075))
52
+ - **misc**: 支持 inbox 的会话功能 ([9b713b8](https://github.com/lobehub/lobe-chat/commit/9b713b8))
53
+ - **misc**: 新增 inbox 数据模型 ([91a8158](https://github.com/lobehub/lobe-chat/commit/91a8158))
54
+ - **misc**: 新增 inbox 模块入口 ([6fc8907](https://github.com/lobehub/lobe-chat/commit/6fc8907))
55
+
56
+ #### Styles
57
+
58
+ - **misc**: Fix Inbox defaultMessage avatar ([dbc18a4](https://github.com/lobehub/lobe-chat/commit/dbc18a4))
59
+ - **misc**: 优化 header 的 setting 展示 ([201d380](https://github.com/lobehub/lobe-chat/commit/201d380))
60
+ - **misc**: 优化门禁下默认的解锁方式 ([df9bb45](https://github.com/lobehub/lobe-chat/commit/df9bb45))
61
+ - **misc**: 补充 ChatList 的 Loading 态 ([eb3eb5d](https://github.com/lobehub/lobe-chat/commit/eb3eb5d))
62
+
63
+ </details>
64
+
65
+ <div align="right">
66
+
67
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
68
+
69
+ </div>
70
+
5
71
  ## [Version 0.43.0](https://github.com/lobehub/lobe-chat/compare/v0.42.3...v0.43.0)
6
72
 
7
73
  <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.43.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",
@@ -25,9 +25,11 @@
25
25
  "dev": "next dev -p 3010",
26
26
  "i18n": "npm run i18n:toc && lobe-i18n",
27
27
  "i18n:toc": "ts-node --project ./tsconfig.json scripts/toc.ts",
28
- "lint": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
28
+ "lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
29
+ "lint:circular": "dpdm src/**/*.ts --warning false --tree false --exit-code circular:1 -T true",
29
30
  "lint:md": "remark . --quiet --frail --output",
30
31
  "lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
32
+ "lint:ts": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
31
33
  "prepare": "husky install",
32
34
  "prettier": "prettier -c --write \"**/**\"",
33
35
  "pull": "git pull",
@@ -116,6 +118,7 @@
116
118
  "@vitest/coverage-v8": "latest",
117
119
  "commitlint": "^17",
118
120
  "consola": "^3",
121
+ "dpdm": "^3",
119
122
  "eslint": "^8",
120
123
  "husky": "^8",
121
124
  "i18next-browser-languagedetector": "^7",
package/src/const/meta.ts CHANGED
@@ -4,3 +4,6 @@ export const DEFAULT_AVATAR = '🤖';
4
4
  export const DEFAULT_USER_AVATAR = '😀';
5
5
  export const DEFAULT_BACKGROUND_COLOR = 'rgba(0,0,0,0)';
6
6
  export const DEFAULT_AGENT_META: MetaData = {};
7
+
8
+ export const DEFAULT_INBOX_AVATAR =
9
+ 'https://npm.elemecdn.com/@lobehub/assets-logo/assets/logo-3d.webp';
@@ -0,0 +1 @@
1
+ export const INBOX_SESSION_ID = 'inbox';
@@ -2,22 +2,25 @@ import { settingsSelectors, useGlobalStore } from '@/store/global';
2
2
  import { sessionSelectors, useSessionStore } from '@/store/session';
3
3
  import { createConfigFile, exportConfigFile } from '@/utils/config';
4
4
 
5
- const getAgents = () => sessionSelectors.exportAgents(useSessionStore.getState());
6
- const getAgent = (id: string) => sessionSelectors.getExportAgent(id)(useSessionStore.getState());
7
-
8
5
  const getSessions = () => sessionSelectors.exportSessions(useSessionStore.getState());
9
6
  const getSession = (id: string) => sessionSelectors.getSessionById(id)(useSessionStore.getState());
10
7
 
11
8
  const getSettings = () => settingsSelectors.exportSettings(useGlobalStore.getState());
12
9
 
10
+ // ============= 导出所有角色 ============= //
11
+
13
12
  export const exportAgents = () => {
14
- const sessions = getAgents();
13
+ const agents = sessionSelectors.exportAgents(useSessionStore.getState());
15
14
 
16
- const config = createConfigFile('agents', { sessions });
15
+ const config = createConfigFile('agents', agents);
17
16
 
18
17
  exportConfigFile(config, 'agents');
19
18
  };
20
19
 
20
+ // ============= 导出单个角色 ============= //
21
+
22
+ const getAgent = (id: string) => sessionSelectors.getExportAgent(id)(useSessionStore.getState());
23
+
21
24
  export const exportSingleAgent = (id: string) => {
22
25
  const agent = getAgent(id);
23
26
  if (!agent) return;
@@ -27,23 +30,26 @@ export const exportSingleAgent = (id: string) => {
27
30
  exportConfigFile(config, agent.meta?.title || 'agent');
28
31
  };
29
32
 
33
+ // ============= 导出所有会话 ============= //
34
+
30
35
  export const exportSessions = () => {
31
- const sessions = getSessions();
32
- const config = createConfigFile('sessions', { sessions });
36
+ const config = createConfigFile('sessions', getSessions());
33
37
 
34
38
  exportConfigFile(config, 'sessions');
35
39
  };
36
40
 
41
+ // ============= 导出单个会话 ============= //
37
42
  export const exportSingleSession = (id: string) => {
38
43
  const session = getSession(id);
39
44
  if (!session) return;
40
45
  const sessions = { [id]: session };
41
46
 
42
- const config = createConfigFile('sessions', { sessions });
47
+ const config = createConfigFile('singleSession', { sessions });
43
48
 
44
49
  exportConfigFile(config, `${session.meta?.title}-session`);
45
50
  };
46
51
 
52
+ // ============= 导出设置会话 ============= //
47
53
  export const exportSettings = () => {
48
54
  const settings = getSettings();
49
55
 
@@ -56,7 +62,7 @@ export const exportAll = () => {
56
62
  const sessions = getSessions();
57
63
  const settings = getSettings();
58
64
 
59
- const config = createConfigFile('all', { sessions, settings });
65
+ const config = createConfigFile('all', { ...sessions, settings });
60
66
 
61
67
  exportConfigFile(config, 'config');
62
68
  };
@@ -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
+ });
@@ -17,7 +17,7 @@ export default {
17
17
  copy: '复制',
18
18
  copySuccess: '复制成功',
19
19
  defaultAgent: '默认助手',
20
- defaultSession: '默认对话',
20
+ defaultSession: '默认助手',
21
21
  delete: '删除',
22
22
  edit: '编辑',
23
23
  export: '导出配置',
@@ -31,6 +31,12 @@ export default {
31
31
  },
32
32
  feedback: '反馈与建议',
33
33
  import: '导入配置',
34
+ inbox: {
35
+ defaultMessage:
36
+ '你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击「+」创建自定义助手',
37
+ desc: '开启大脑集群,激发思维火花。你的智能助理,就在这里与你交流一切',
38
+ title: '随便聊聊',
39
+ },
34
40
  message: {
35
41
  function_loading: '插件请求中...',
36
42
  },
@@ -47,6 +53,7 @@ export default {
47
53
  searchAgentPlaceholder: '搜索助手和对话...',
48
54
  send: '发送',
49
55
  sendPlaceholder: '输入聊天内容...',
56
+ sessionList: '助手列表',
50
57
  setting: '设置',
51
58
  share: '分享',
52
59
  tab: {
@@ -0,0 +1,25 @@
1
+ import { Avatar, List } from '@lobehub/ui';
2
+ import Link from 'next/link';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { DEFAULT_INBOX_AVATAR } from '@/const/meta';
7
+ import { INBOX_SESSION_ID } from '@/const/session';
8
+ import { useSessionStore } from '@/store/session';
9
+
10
+ const Inbox = memo(() => {
11
+ const { t } = useTranslation('common');
12
+ const [activeId] = useSessionStore((s) => [s.activeId]);
13
+
14
+ return (
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>
22
+ );
23
+ });
24
+
25
+ export default Inbox;
@@ -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;
@@ -1,28 +1,28 @@
1
1
  import isEqual from 'fast-deep-equal';
2
2
  import Link from 'next/link';
3
3
  import { memo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
4
5
 
5
6
  import { sessionSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
6
7
 
7
- import SessionItem from './SessionItem';
8
- import SkeletonItem from './SkeletonItem';
8
+ import SessionItem from './Item';
9
+ import SkeletonList from './SkeletonList';
9
10
 
10
11
  const SessionList = memo(() => {
11
12
  const list = useSessionStore(sessionSelectors.sessionList, isEqual);
12
- const [activeId, loading] = useSessionStore((s) => [s.activeId, s.autocompleteLoading.title]);
13
13
 
14
14
  const isInit = useSessionHydrated();
15
15
 
16
16
  return !isInit ? (
17
- <SkeletonItem />
17
+ <SkeletonList />
18
18
  ) : (
19
- <>
19
+ <Flexbox>
20
20
  {list.map(({ id }) => (
21
21
  <Link href={`/chat/${id}`} key={id}>
22
- <SessionItem active={activeId === id} id={id} loading={loading && id === activeId} />
22
+ <SessionItem id={id} />
23
23
  </Link>
24
24
  ))}
25
- </>
25
+ </Flexbox>
26
26
  );
27
27
  });
28
28
 
@@ -1,17 +1,29 @@
1
1
  import { DraggablePanelBody } from '@lobehub/ui';
2
2
  import { memo } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Flexbox } from 'react-layout-kit';
3
5
 
4
6
  import FolderPanel from '@/features/FolderPanel';
5
7
 
6
8
  import Header from './Header';
9
+ import Inbox from './Inbox';
7
10
  import SessionList from './List';
8
11
 
9
12
  export const Sessions = memo(() => {
13
+ const { t } = useTranslation('common');
14
+
10
15
  return (
11
16
  <FolderPanel>
12
17
  <Header />
13
18
  <DraggablePanelBody style={{ padding: 0 }}>
14
- <SessionList />
19
+ <Flexbox gap={8}>
20
+ <Inbox />
21
+
22
+ <Flexbox paddingInline={20} style={{ fontSize: 12, marginTop: 8 }}>
23
+ {t('sessionList')}
24
+ </Flexbox>
25
+ <SessionList />
26
+ </Flexbox>
15
27
  </DraggablePanelBody>
16
28
  </FolderPanel>
17
29
  );
@@ -5,9 +5,9 @@ import { Flexbox } from 'react-layout-kit';
5
5
  import { agentSelectors, useSessionStore } from '@/store/session';
6
6
  import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
7
7
 
8
- import Conversation from './Conversation';
9
- import Header from './Header';
10
- import Config from './Sidebar';
8
+ import Conversation from '../features/Conversation';
9
+ import Header from '../features/Header';
10
+ import SideBar from '../features/Sidebar';
11
11
  import Layout from './layout';
12
12
 
13
13
  const Chat = memo(() => {
@@ -24,12 +24,10 @@ const Chat = memo(() => {
24
24
  <title>{pageTitle}</title>
25
25
  </Head>
26
26
  <Layout>
27
- <Flexbox id={'lobe-conversion-container'} style={{ height: '100vh' }}>
28
- <Header />
29
- <Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
30
- <Conversation />
31
- <Config />
32
- </Flexbox>
27
+ <Header />
28
+ <Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
29
+ <Conversation />
30
+ <SideBar />
33
31
  </Flexbox>
34
32
  </Layout>
35
33
  </>
@@ -1,15 +1,12 @@
1
1
  import { useRouter } from 'next/router';
2
- import { PropsWithChildren, memo, useEffect } from 'react';
3
- import { Flexbox } from 'react-layout-kit';
2
+ import { PropsWithChildren, memo } from 'react';
4
3
  import { shallow } from 'zustand/shallow';
5
4
 
6
- import AppLayout from '@/layout/AppLayout';
7
- import { useSwitchSideBarOnInit } from '@/store/global';
8
- import { useSessionStore } from '@/store/session';
5
+ import { useOnFinishHydrationSession, useSessionStore } from '@/store/session';
9
6
 
10
- import { Sessions } from '../SessionList';
7
+ import ChatLayout from '../layout';
11
8
 
12
- const ChatLayout = memo<PropsWithChildren>(({ children }) => {
9
+ const Layout = memo<PropsWithChildren>(({ children }) => {
13
10
  const [activeSession, toggleTopic] = useSessionStore((s) => {
14
11
  return [s.activeSession, s.toggleTopic];
15
12
  }, shallow);
@@ -17,11 +14,7 @@ const ChatLayout = memo<PropsWithChildren>(({ children }) => {
17
14
  const router = useRouter();
18
15
  const { id } = router.query;
19
16
 
20
- useEffect(() => {
21
- const hasRehydrated = useSessionStore.persist.hasHydrated();
22
- // 只有当水合完毕后再开始做操作
23
- if (!hasRehydrated) return;
24
-
17
+ useOnFinishHydrationSession(() => {
25
18
  // 1. 正常激活会话
26
19
  if (typeof id === 'string') {
27
20
  activeSession(id);
@@ -31,16 +24,7 @@ const ChatLayout = memo<PropsWithChildren>(({ children }) => {
31
24
  toggleTopic();
32
25
  }, [id]);
33
26
 
34
- useSwitchSideBarOnInit('chat');
35
-
36
- return (
37
- <AppLayout>
38
- <Sessions />
39
- <Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
40
- {children}
41
- </Flexbox>
42
- </AppLayout>
43
- );
27
+ return <ChatLayout>{children}</ChatLayout>;
44
28
  });
45
29
 
46
- export default ChatLayout;
30
+ export default Layout;