@lobehub/chat 0.41.2 → 0.42.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/CHANGELOG.md +25 -0
- package/locales/en_US/error.json +1 -0
- package/locales/en_US/plugin.json +5 -0
- package/locales/en_US/welcome.json +14 -0
- package/locales/zh_CN/error.json +1 -0
- package/locales/zh_CN/plugin.json +5 -0
- package/locales/zh_CN/welcome.json +14 -0
- package/package.json +1 -1
- package/src/components/DiscordIcon/index.tsx +15 -0
- package/src/const/url.ts +7 -0
- package/src/features/SideBar/BottomActions.tsx +7 -6
- package/src/features/SideBar/TopActions.tsx +1 -1
- package/src/layout/AppLayout.tsx +15 -0
- package/src/locales/default/common.ts +1 -0
- package/src/locales/default/welcome.ts +14 -0
- package/src/locales/resources/zh_CN.ts +2 -0
- package/src/pages/chat/SessionList/List/index.tsx +1 -1
- package/src/pages/chat/[id]/Sidebar/Inner.tsx +41 -23
- package/src/pages/chat/[id]/index.page.tsx +1 -1
- package/src/pages/chat/{layout.tsx → [id]/layout.tsx} +6 -13
- package/src/pages/chat/[id]/setting/index.page.tsx +3 -3
- package/src/pages/chat/index.page.tsx +1 -24
- package/src/pages/index.page.tsx +33 -1
- package/src/pages/welcome/Banner/AgentCard.tsx +71 -0
- package/src/pages/welcome/Banner/AgentTemplate.tsx +43 -0
- package/src/pages/welcome/Banner/Hero.tsx +39 -0
- package/src/pages/welcome/Banner/index.tsx +56 -0
- package/src/pages/welcome/Footer/index.tsx +24 -0
- package/src/pages/welcome/index.page.tsx +28 -0
- package/src/pages/welcome/layout.tsx +31 -0
- package/src/pages/welcome/style.ts +63 -0
- package/src/store/session/slices/session/selectors/index.ts +2 -0
- package/src/store/session/slices/session/selectors/list.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.42.0](https://github.com/lobehub/lobe-chat/compare/v0.41.2...v0.42.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-08-11**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Add `Welcome` page.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Add `Welcome` page, closes [#60](https://github.com/lobehub/lobe-chat/issues/60) ([810ab0f](https://github.com/lobehub/lobe-chat/commit/810ab0f))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
### [Version 0.41.2](https://github.com/lobehub/lobe-chat/compare/v0.41.1...v0.41.2)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2023-08-10**</sup>
|
package/locales/en_US/error.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"401": "Sorry, the server has rejected your request. This may be due to insufficient permissions or invalid authentication.",
|
|
5
5
|
"403": "Sorry, the server has rejected your request. You do not have permission to access this content.",
|
|
6
6
|
"404": "Sorry, the server cannot find the page or resource you requested. Please make sure your URL is correct.",
|
|
7
|
+
"405": "Sorry, the server does not support the request method you are using. Please make sure your request method is correct.",
|
|
7
8
|
"429": "Sorry, your request is too frequent and the server is a bit tired. Please try again later.",
|
|
8
9
|
"500": "Sorry, the server seems to be experiencing some difficulties and is temporarily unable to complete your request. Please try again later.",
|
|
9
10
|
"502": "Sorry, the server seems to be lost and is temporarily unable to provide service. Please try again later.",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"button": {
|
|
3
|
+
"import": "Import Configuration",
|
|
4
|
+
"start": "Start Now"
|
|
5
|
+
},
|
|
6
|
+
"header": "Welcome",
|
|
7
|
+
"pickAgent": "Or choose from the following assistant templates",
|
|
8
|
+
"skip": "Skip Creation",
|
|
9
|
+
"slogan": {
|
|
10
|
+
"desc1": "Unlock the power of your brain and ignite your creativity. Your intelligent assistant is always here.",
|
|
11
|
+
"desc2": "Create your first assistant and let's get started~",
|
|
12
|
+
"title": "Give yourself a smarter brain"
|
|
13
|
+
}
|
|
14
|
+
}
|
package/locales/zh_CN/error.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"401": "很抱歉,服务器拒绝了您的请求,可能是因为您的权限不足或未提供有效的身份验证",
|
|
5
5
|
"403": "很抱歉,服务器拒绝了您的请求,您没有访问此内容的权限 ",
|
|
6
6
|
"404": "很抱歉,服务器找不到您请求的页面或资源,请确认您的 URL 是否正确",
|
|
7
|
+
"405": "很抱歉,服务器不支持您使用的请求方法,请确认您的请求方法是否正确",
|
|
7
8
|
"429": "很抱歉,您的请求太多,服务器有点累了,请稍后再试",
|
|
8
9
|
"500": "很抱歉,服务器似乎遇到了一些困难,暂时无法完成您的请求,请稍后再试",
|
|
9
10
|
"502": "很抱歉,服务器似乎迷失了方向,暂时无法提供服务,请稍后再试",
|
package/package.json
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type LucideIcon, createLucideIcon } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
const Discord: LucideIcon = createLucideIcon('Discord', [
|
|
4
|
+
[
|
|
5
|
+
'path',
|
|
6
|
+
{
|
|
7
|
+
d: 'M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z',
|
|
8
|
+
key: '18tl5t',
|
|
9
|
+
},
|
|
10
|
+
],
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
const DiscordIcon = (props: LucideIcon) => <Discord style={{ overflow: 'visible' }} {...props} />;
|
|
14
|
+
|
|
15
|
+
export default DiscordIcon as LucideIcon;
|
package/src/const/url.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import pkg from '../../package.json';
|
|
2
|
+
|
|
3
|
+
export const GITHUB = pkg.homepage;
|
|
4
|
+
export const CHANGELOG = `${pkg.homepage}/blob/master/CHANGELOG.md`;
|
|
5
|
+
export const ABOUT = pkg.homepage;
|
|
6
|
+
export const FEEDBACK = pkg.bugs.url;
|
|
7
|
+
export const DISCORD = 'https://discord.gg/AYFPHvv2jT';
|
|
@@ -14,12 +14,12 @@ import Router from 'next/router';
|
|
|
14
14
|
import { memo, useMemo } from 'react';
|
|
15
15
|
import { useTranslation } from 'react-i18next';
|
|
16
16
|
|
|
17
|
+
import DiscordIcon from '@/components/DiscordIcon';
|
|
18
|
+
import { ABOUT, CHANGELOG, DISCORD, FEEDBACK, GITHUB } from '@/const/url';
|
|
17
19
|
import { useExportConfig } from '@/hooks/useExportConfig';
|
|
18
20
|
import { useImportConfig } from '@/hooks/useImportConfig';
|
|
19
21
|
import { SettingsStore } from '@/store/settings';
|
|
20
22
|
|
|
21
|
-
import pkg from '../../../package.json';
|
|
22
|
-
|
|
23
23
|
export interface BottomActionProps {
|
|
24
24
|
setTab: SettingsStore['switchSideBar'];
|
|
25
25
|
tab: SettingsStore['sidebarKey'];
|
|
@@ -79,19 +79,19 @@ const BottomActions = memo<BottomActionProps>(({ tab, setTab }) => {
|
|
|
79
79
|
icon: <Icon icon={Feather} />,
|
|
80
80
|
key: 'feedback',
|
|
81
81
|
label: t('feedback'),
|
|
82
|
-
onClick: () => window.open(
|
|
82
|
+
onClick: () => window.open(FEEDBACK, '__blank'),
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
icon: <Icon icon={FileClock} />,
|
|
86
86
|
key: 'changelog',
|
|
87
87
|
label: t('changelog'),
|
|
88
|
-
onClick: () => window.open(
|
|
88
|
+
onClick: () => window.open(CHANGELOG, '__blank'),
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
icon: <Icon icon={Heart} />,
|
|
92
92
|
key: 'about',
|
|
93
93
|
label: t('about'),
|
|
94
|
-
onClick: () => window.open(
|
|
94
|
+
onClick: () => window.open(ABOUT, '__blank'),
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
type: 'divider',
|
|
@@ -111,7 +111,8 @@ const BottomActions = memo<BottomActionProps>(({ tab, setTab }) => {
|
|
|
111
111
|
|
|
112
112
|
return (
|
|
113
113
|
<>
|
|
114
|
-
<ActionIcon icon={
|
|
114
|
+
<ActionIcon icon={DiscordIcon} onClick={() => window.open(DISCORD, '__blank')} />
|
|
115
|
+
<ActionIcon icon={Github} onClick={() => window.open(GITHUB, '__blank')} />
|
|
115
116
|
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
|
116
117
|
<ActionIcon active={tab === 'settings'} icon={Settings2} />
|
|
117
118
|
</Dropdown>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { PropsWithChildren, memo } from 'react';
|
|
2
|
+
import { Flexbox } from 'react-layout-kit';
|
|
3
|
+
|
|
4
|
+
import SideBar from '@/features/SideBar';
|
|
5
|
+
|
|
6
|
+
const AppLayout = memo<PropsWithChildren>(({ children }) => {
|
|
7
|
+
return (
|
|
8
|
+
<Flexbox horizontal width={'100%'}>
|
|
9
|
+
<SideBar />
|
|
10
|
+
{children}
|
|
11
|
+
</Flexbox>
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default AppLayout;
|
|
@@ -3,6 +3,7 @@ import empty from '../default/empty';
|
|
|
3
3
|
import error from '../default/error';
|
|
4
4
|
import plugin from '../default/plugin';
|
|
5
5
|
import setting from '../default/setting';
|
|
6
|
+
import welcome from '../default/welcome';
|
|
6
7
|
|
|
7
8
|
const resources = {
|
|
8
9
|
common,
|
|
@@ -10,6 +11,7 @@ const resources = {
|
|
|
10
11
|
error,
|
|
11
12
|
plugin,
|
|
12
13
|
setting,
|
|
14
|
+
welcome,
|
|
13
15
|
} as const;
|
|
14
16
|
|
|
15
17
|
export default resources;
|
|
@@ -8,7 +8,7 @@ import SessionItem from './SessionItem';
|
|
|
8
8
|
import SkeletonItem from './SkeletonItem';
|
|
9
9
|
|
|
10
10
|
const SessionList = memo(() => {
|
|
11
|
-
const list = useSessionStore(
|
|
11
|
+
const list = useSessionStore(sessionSelectors.sessionList, isEqual);
|
|
12
12
|
const [activeId, loading] = useSessionStore((s) => [s.activeId, s.autocompleteLoading.title]);
|
|
13
13
|
|
|
14
14
|
const isInit = useSessionHydrated();
|
|
@@ -28,13 +28,28 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
28
28
|
opacity: 1;
|
|
29
29
|
}
|
|
30
30
|
`,
|
|
31
|
+
promptBox: css`
|
|
32
|
+
position: relative;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
border-bottom: 1px solid ${token.colorBorder};
|
|
35
|
+
`,
|
|
36
|
+
promptMask: css`
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
|
|
39
|
+
position: absolute;
|
|
40
|
+
z-index: 10;
|
|
41
|
+
bottom: 0;
|
|
42
|
+
left: 0;
|
|
43
|
+
|
|
44
|
+
width: 100%;
|
|
45
|
+
height: 32px;
|
|
46
|
+
|
|
47
|
+
background: linear-gradient(to bottom, transparent, ${token.colorBgLayout});
|
|
48
|
+
`,
|
|
31
49
|
title: css`
|
|
32
50
|
font-size: ${token.fontSizeHeading4}px;
|
|
33
51
|
font-weight: bold;
|
|
34
52
|
`,
|
|
35
|
-
topic: css`
|
|
36
|
-
border-top: 1px solid ${token.colorBorder};
|
|
37
|
-
`,
|
|
38
53
|
}));
|
|
39
54
|
|
|
40
55
|
const Inner = memo(() => {
|
|
@@ -60,33 +75,36 @@ const Inner = memo(() => {
|
|
|
60
75
|
}
|
|
61
76
|
title={t('settingAgent.prompt.title', { ns: 'setting' })}
|
|
62
77
|
/>
|
|
63
|
-
<Flexbox height={200} padding={'0 16px 16px'}>
|
|
78
|
+
<Flexbox className={styles.promptBox} height={200} padding={'0 16px 16px'}>
|
|
64
79
|
{hydrated ? (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
<>
|
|
81
|
+
<EditableMessage
|
|
82
|
+
classNames={{ markdown: styles.prompt }}
|
|
83
|
+
onChange={(e) => {
|
|
84
|
+
updateAgentConfig({ systemRole: e });
|
|
85
|
+
}}
|
|
86
|
+
onOpenChange={setOpenModal}
|
|
87
|
+
openModal={openModal}
|
|
88
|
+
placeholder={`${t('settingAgent.prompt.placeholder', { ns: 'setting' })}...`}
|
|
89
|
+
styles={{ markdown: systemRole ? {} : { opacity: 0.5 } }}
|
|
90
|
+
text={{
|
|
91
|
+
cancel: t('cancel'),
|
|
92
|
+
confirm: t('ok'),
|
|
93
|
+
edit: t('edit'),
|
|
94
|
+
title: t('settingAgent.prompt.title', { ns: 'setting' }),
|
|
95
|
+
}}
|
|
96
|
+
value={systemRole}
|
|
97
|
+
/>
|
|
98
|
+
<div className={styles.promptMask} />
|
|
99
|
+
</>
|
|
82
100
|
) : (
|
|
83
101
|
<Skeleton active avatar={false} style={{ marginTop: 12 }} title={false} />
|
|
84
102
|
)}
|
|
85
103
|
</Flexbox>
|
|
86
|
-
<Flexbox
|
|
104
|
+
<Flexbox gap={16} padding={16}>
|
|
87
105
|
<SearchBar placeholder={t('topic.searchPlaceholder')} spotlight type={'ghost'} />
|
|
88
106
|
{!hydrated ? (
|
|
89
|
-
<Flexbox gap={8} style={{ marginTop:
|
|
107
|
+
<Flexbox gap={8} style={{ marginTop: 8 }}>
|
|
90
108
|
{Array.from({ length: 8 }).map((_, i) => (
|
|
91
109
|
<Skeleton
|
|
92
110
|
active
|
|
@@ -5,10 +5,10 @@ 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 Layout from '../layout';
|
|
9
8
|
import Conversation from './Conversation';
|
|
10
9
|
import Header from './Header';
|
|
11
10
|
import Config from './Sidebar';
|
|
11
|
+
import Layout from './layout';
|
|
12
12
|
|
|
13
13
|
const Chat = memo(() => {
|
|
14
14
|
const [avatar, title] = useSessionStore((s) => [
|
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
import { useRouter } from 'next/router';
|
|
2
2
|
import { PropsWithChildren, memo, useEffect } from 'react';
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
import { shallow } from 'zustand/shallow';
|
|
4
5
|
|
|
5
|
-
import
|
|
6
|
-
import { createI18nNext } from '@/locales/create';
|
|
6
|
+
import AppLayout from '@/layout/AppLayout';
|
|
7
7
|
import { useSessionStore } from '@/store/session';
|
|
8
8
|
import { useSettings } from '@/store/settings';
|
|
9
9
|
|
|
10
|
-
import { Sessions } from '
|
|
11
|
-
|
|
12
|
-
const initI18n = createI18nNext();
|
|
10
|
+
import { Sessions } from '../SessionList';
|
|
13
11
|
|
|
14
12
|
const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
|
15
|
-
useEffect(() => {
|
|
16
|
-
initI18n.finally();
|
|
17
|
-
}, []);
|
|
18
|
-
|
|
19
13
|
const [activeSession, toggleTopic] = useSessionStore((s) => {
|
|
20
14
|
return [s.activeSession, s.toggleTopic];
|
|
21
|
-
});
|
|
15
|
+
}, shallow);
|
|
22
16
|
|
|
23
17
|
const router = useRouter();
|
|
24
18
|
const { id } = router.query;
|
|
@@ -45,13 +39,12 @@ const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
|
|
45
39
|
}, []);
|
|
46
40
|
|
|
47
41
|
return (
|
|
48
|
-
<
|
|
49
|
-
<SideBar />
|
|
42
|
+
<AppLayout>
|
|
50
43
|
<Sessions />
|
|
51
44
|
<Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
|
|
52
45
|
{children}
|
|
53
46
|
</Flexbox>
|
|
54
|
-
</
|
|
47
|
+
</AppLayout>
|
|
55
48
|
);
|
|
56
49
|
});
|
|
57
50
|
|
|
@@ -7,10 +7,10 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
7
7
|
import HeaderSpacing from '@/components/HeaderSpacing';
|
|
8
8
|
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
|
9
9
|
import { AgentConfig, AgentMeta, AgentPlugin, AgentPrompt } from '@/features/AgentSetting';
|
|
10
|
+
import AppLayout from '@/layout/AppLayout';
|
|
10
11
|
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
11
12
|
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
|
12
13
|
|
|
13
|
-
import ChatLayout from '../../layout';
|
|
14
14
|
import Header from './Header';
|
|
15
15
|
|
|
16
16
|
const EditPage = memo(() => {
|
|
@@ -32,7 +32,7 @@ const EditPage = memo(() => {
|
|
|
32
32
|
<Head>
|
|
33
33
|
<title>{pageTitle}</title>
|
|
34
34
|
</Head>
|
|
35
|
-
<
|
|
35
|
+
<AppLayout>
|
|
36
36
|
<Header />
|
|
37
37
|
<Flexbox align={'center'} flex={1} gap={16} padding={24} style={{ overflow: 'auto' }}>
|
|
38
38
|
<HeaderSpacing height={HEADER_HEIGHT - 16} />
|
|
@@ -46,7 +46,7 @@ const EditPage = memo(() => {
|
|
|
46
46
|
<AgentConfig config={config} updateConfig={updateAgentConfig} />
|
|
47
47
|
<AgentPlugin config={config} updateConfig={toggleAgentPlugin} />
|
|
48
48
|
</Flexbox>
|
|
49
|
-
</
|
|
49
|
+
</AppLayout>
|
|
50
50
|
</>
|
|
51
51
|
);
|
|
52
52
|
});
|
|
@@ -1,24 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { useEffect } from 'react';
|
|
3
|
-
|
|
4
|
-
import { sessionSelectors, useSessionStore } from '@/store/session';
|
|
5
|
-
|
|
6
|
-
import Chat from './[id]/index.page';
|
|
7
|
-
|
|
8
|
-
export default () => {
|
|
9
|
-
// 支持用户在进到首页时,自动激活列表中的第一个角色
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
const hasRehydrated = useSessionStore.persist.hasHydrated();
|
|
12
|
-
// 只有当水合完毕后,才往下走
|
|
13
|
-
if (!hasRehydrated) return;
|
|
14
|
-
|
|
15
|
-
// 如果当前有会话,那么就激活第一个会话
|
|
16
|
-
const list = sessionSelectors.sessionList(useSessionStore.getState());
|
|
17
|
-
if (list.length > 0) {
|
|
18
|
-
const sessionId = list[0].id;
|
|
19
|
-
Router.push(`/chat/${sessionId}`);
|
|
20
|
-
}
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
return <Chat />;
|
|
24
|
-
};
|
|
1
|
+
export { default } from './[id]/index.page';
|
package/src/pages/index.page.tsx
CHANGED
|
@@ -1 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
import { Icon } from '@lobehub/ui';
|
|
2
|
+
import { useWhyDidYouUpdate } from 'ahooks';
|
|
3
|
+
import { Loader2 } from 'lucide-react';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Center } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import { sessionSelectors, useSessionHydrated, useSessionStore } from '@/store/session';
|
|
9
|
+
|
|
10
|
+
import Chat from './chat/index.page';
|
|
11
|
+
import Welcome from './welcome/index.page';
|
|
12
|
+
|
|
13
|
+
const Loading = memo(() => {
|
|
14
|
+
const { t } = useTranslation('common');
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Center gap={12} height={'100vh'} width={'100%'}>
|
|
18
|
+
<Icon icon={Loader2} size={{ fontSize: 64 }} spin />
|
|
19
|
+
{t('appInitializing')}
|
|
20
|
+
</Center>
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default memo(() => {
|
|
25
|
+
const hydrated = useSessionHydrated();
|
|
26
|
+
const hasSession = useSessionStore(sessionSelectors.hasSessionList);
|
|
27
|
+
|
|
28
|
+
useWhyDidYouUpdate('index.page', { hasSession, hydrated });
|
|
29
|
+
|
|
30
|
+
if (!hydrated) return <Loading />;
|
|
31
|
+
|
|
32
|
+
return !hasSession ? <Welcome /> : <Chat />;
|
|
33
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Avatar, Spotlight } from '@lobehub/ui';
|
|
2
|
+
import { Typography } from 'antd';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import { rgba } from 'polished';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import { LobeAgentSession } from '@/types/session';
|
|
9
|
+
|
|
10
|
+
const { Paragraph, Title } = Typography;
|
|
11
|
+
|
|
12
|
+
const useStyles = createStyles(({ css, token, cx, stylish }) => ({
|
|
13
|
+
container: cx(
|
|
14
|
+
stylish.blur,
|
|
15
|
+
css`
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
|
|
18
|
+
position: relative;
|
|
19
|
+
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
flex: 1;
|
|
22
|
+
|
|
23
|
+
padding: 16px;
|
|
24
|
+
|
|
25
|
+
background-color: ${rgba(token.colorBgContainer, 0.5)};
|
|
26
|
+
border: 1px solid ${rgba(token.colorText, 0.2)};
|
|
27
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
28
|
+
|
|
29
|
+
transition: all 400ms ${token.motionEaseOut};
|
|
30
|
+
|
|
31
|
+
&:hover {
|
|
32
|
+
background-color: ${rgba(token.colorBgElevated, 0.2)};
|
|
33
|
+
border-color: ${token.colorText};
|
|
34
|
+
box-shadow: 0 0 0 1px ${token.colorText};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
,
|
|
38
|
+
&:active {
|
|
39
|
+
scale: 0.98;
|
|
40
|
+
}
|
|
41
|
+
`,
|
|
42
|
+
),
|
|
43
|
+
desc: css`
|
|
44
|
+
margin: 0 !important;
|
|
45
|
+
`,
|
|
46
|
+
title: css`
|
|
47
|
+
margin: 0 !important;
|
|
48
|
+
`,
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
export interface AgentCardProps {
|
|
52
|
+
meta: LobeAgentSession['meta'];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const AgentCard = memo<AgentCardProps>(({ meta }) => {
|
|
56
|
+
const { styles } = useStyles();
|
|
57
|
+
return (
|
|
58
|
+
<Flexbox className={styles.container} gap={8}>
|
|
59
|
+
<Spotlight size={200} />
|
|
60
|
+
<Avatar avatar={meta.avatar} background={meta.backgroundColor} />
|
|
61
|
+
<Title className={styles.title} ellipsis level={5}>
|
|
62
|
+
{meta.title}
|
|
63
|
+
</Title>
|
|
64
|
+
<Paragraph className={styles.desc} ellipsis={{ rows: 2 }} type="secondary">
|
|
65
|
+
{meta.description}
|
|
66
|
+
</Paragraph>
|
|
67
|
+
</Flexbox>
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export default AgentCard;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useResponsive } from 'antd-style';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
|
4
|
+
|
|
5
|
+
import { useStyles } from '../style';
|
|
6
|
+
import AgentCard, { type AgentCardProps } from './AgentCard';
|
|
7
|
+
|
|
8
|
+
const items: AgentCardProps['meta'][] = [
|
|
9
|
+
{
|
|
10
|
+
avatar: '😀',
|
|
11
|
+
description: 'dddddd',
|
|
12
|
+
title: 'Title',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
avatar: '😀',
|
|
16
|
+
description: 'dddddd',
|
|
17
|
+
title: 'Title',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
avatar: '😀',
|
|
21
|
+
description: 'dddddd',
|
|
22
|
+
title: 'Title',
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const AgentTemplate = memo<{ width: number }>(({ width }) => {
|
|
27
|
+
const { styles } = useStyles();
|
|
28
|
+
const { mobile } = useResponsive();
|
|
29
|
+
return (
|
|
30
|
+
<Flexbox
|
|
31
|
+
className={styles.templateContainer}
|
|
32
|
+
gap={16}
|
|
33
|
+
horizontal={!mobile}
|
|
34
|
+
style={{ marginTop: -width / 20 }}
|
|
35
|
+
>
|
|
36
|
+
{items.map((meta, index) => (
|
|
37
|
+
<AgentCard key={index} meta={meta} />
|
|
38
|
+
))}
|
|
39
|
+
</Flexbox>
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export default AgentTemplate;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LogoThree } from '@lobehub/ui';
|
|
2
|
+
import { useResponsive } from 'antd-style';
|
|
3
|
+
import { memo, useMemo } from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
import { genSize, useStyles } from '../style';
|
|
7
|
+
|
|
8
|
+
const Hero = memo<{ width: number }>(({ width }) => {
|
|
9
|
+
const size = useMemo(
|
|
10
|
+
() => ({
|
|
11
|
+
base: genSize(width / 3.5, 240),
|
|
12
|
+
desc: genSize(width / 50, 14),
|
|
13
|
+
logo: genSize(width / 3.8, 180),
|
|
14
|
+
title: genSize(width / 20, 32),
|
|
15
|
+
}),
|
|
16
|
+
[width],
|
|
17
|
+
);
|
|
18
|
+
const { styles } = useStyles(size.base);
|
|
19
|
+
const { mobile } = useResponsive();
|
|
20
|
+
const { t } = useTranslation('welcome');
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<LogoThree
|
|
25
|
+
size={size.logo}
|
|
26
|
+
style={{ marginBottom: -size.logo / 40, marginTop: -size.logo / 5 }}
|
|
27
|
+
/>
|
|
28
|
+
<div className={styles.title} style={{ fontSize: size.title }}>
|
|
29
|
+
LobeChat{mobile ? <br /> : ' '}
|
|
30
|
+
{t('slogan.title')}
|
|
31
|
+
</div>
|
|
32
|
+
<div className={styles.desc} style={{ fontSize: size.desc }}>
|
|
33
|
+
{t('slogan.desc1')}
|
|
34
|
+
</div>
|
|
35
|
+
</>
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export default Hero;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { GridShowcase, Icon } from '@lobehub/ui';
|
|
2
|
+
import { useSize } from 'ahooks';
|
|
3
|
+
import { Button, Upload } from 'antd';
|
|
4
|
+
import { SendHorizonal } from 'lucide-react';
|
|
5
|
+
import Link from 'next/link';
|
|
6
|
+
import Router from 'next/router';
|
|
7
|
+
import { memo, useCallback, useRef } from 'react';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { Flexbox } from 'react-layout-kit';
|
|
10
|
+
|
|
11
|
+
import { useImportConfig } from '@/hooks/useImportConfig';
|
|
12
|
+
|
|
13
|
+
import { useStyles } from '../style';
|
|
14
|
+
import AgentTemplate from './AgentTemplate';
|
|
15
|
+
import Hero from './Hero';
|
|
16
|
+
|
|
17
|
+
const Banner = memo(() => {
|
|
18
|
+
const { importConfig } = useImportConfig();
|
|
19
|
+
const ref = useRef(null);
|
|
20
|
+
const domSize = useSize(ref);
|
|
21
|
+
const width = domSize?.width || 1024;
|
|
22
|
+
const { t } = useTranslation('welcome');
|
|
23
|
+
|
|
24
|
+
const { styles } = useStyles();
|
|
25
|
+
|
|
26
|
+
const handleImport = useCallback((e: any) => {
|
|
27
|
+
importConfig(e);
|
|
28
|
+
Router.push('/chat');
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<GridShowcase>
|
|
34
|
+
<div className={styles.container} ref={ref}>
|
|
35
|
+
<Hero width={width} />
|
|
36
|
+
</div>
|
|
37
|
+
<Flexbox gap={16} horizontal style={{ marginTop: 16 }}>
|
|
38
|
+
<Link href={'/chat'}>
|
|
39
|
+
<Button size={'large'} type={'primary'}>
|
|
40
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
|
41
|
+
{t('button.start')}
|
|
42
|
+
<Icon icon={SendHorizonal} />
|
|
43
|
+
</Flexbox>
|
|
44
|
+
</Button>
|
|
45
|
+
</Link>
|
|
46
|
+
<Upload maxCount={1} onChange={handleImport} showUploadList={false}>
|
|
47
|
+
<Button size={'large'}>{t('button.import')}</Button>
|
|
48
|
+
</Upload>
|
|
49
|
+
</Flexbox>
|
|
50
|
+
</GridShowcase>
|
|
51
|
+
<AgentTemplate width={width} />
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export default Banner;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ActionIcon } from '@lobehub/ui';
|
|
2
|
+
import { useTheme } from 'antd-style';
|
|
3
|
+
import { Book, Github } from 'lucide-react';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
|
6
|
+
|
|
7
|
+
import Discord from '@/components/DiscordIcon';
|
|
8
|
+
import { CHANGELOG, DISCORD, GITHUB } from '@/const/url';
|
|
9
|
+
|
|
10
|
+
const Footer = memo(() => {
|
|
11
|
+
const theme = useTheme();
|
|
12
|
+
return (
|
|
13
|
+
<Flexbox align={'center'} horizontal justify={'space-between'} style={{ padding: 16 }}>
|
|
14
|
+
<span style={{ color: theme.colorTextDescription }}>©{new Date().getFullYear()} LobeHub</span>
|
|
15
|
+
<Flexbox horizontal>
|
|
16
|
+
<ActionIcon icon={Discord} onClick={() => window.open(DISCORD, '__blank')} size={'site'} />
|
|
17
|
+
<ActionIcon icon={Book} onClick={() => window.open(CHANGELOG, '__blank')} size={'site'} />
|
|
18
|
+
<ActionIcon icon={Github} onClick={() => window.open(GITHUB, '__blank')} size={'site'} />
|
|
19
|
+
</Flexbox>
|
|
20
|
+
</Flexbox>
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export default Footer;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Head from 'next/head';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
|
|
5
|
+
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
|
6
|
+
|
|
7
|
+
import Banner from './Banner';
|
|
8
|
+
import Footer from './Footer';
|
|
9
|
+
import Layout from './layout';
|
|
10
|
+
|
|
11
|
+
const Welcome = memo(() => {
|
|
12
|
+
const { t } = useTranslation('welcome');
|
|
13
|
+
const pageTitle = genSiteHeadTitle(t('header'));
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
<Head>
|
|
18
|
+
<title>{pageTitle}</title>
|
|
19
|
+
</Head>
|
|
20
|
+
<Layout>
|
|
21
|
+
<Banner />
|
|
22
|
+
<Footer />
|
|
23
|
+
</Layout>
|
|
24
|
+
</>
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
export default Welcome;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Logo } from '@lobehub/ui';
|
|
2
|
+
import Link from 'next/link';
|
|
3
|
+
import { PropsWithChildren, memo } from 'react';
|
|
4
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
import AppLayout from '../../layout/AppLayout';
|
|
7
|
+
import { useStyles } from './style';
|
|
8
|
+
|
|
9
|
+
const WelcomeLayout = memo<PropsWithChildren>(({ children }) => {
|
|
10
|
+
const { styles } = useStyles();
|
|
11
|
+
return (
|
|
12
|
+
<AppLayout>
|
|
13
|
+
<Center
|
|
14
|
+
className={styles.layout}
|
|
15
|
+
flex={1}
|
|
16
|
+
height={'100vh'}
|
|
17
|
+
horizontal
|
|
18
|
+
style={{ position: 'relative' }}
|
|
19
|
+
>
|
|
20
|
+
<Link href={'/'}>
|
|
21
|
+
<Logo className={styles.logo} size={36} type={'text'} />
|
|
22
|
+
</Link>
|
|
23
|
+
<Flexbox className={styles.view} flex={1} style={{ maxWidth: 1024 }}>
|
|
24
|
+
{children}
|
|
25
|
+
</Flexbox>
|
|
26
|
+
</Center>
|
|
27
|
+
</AppLayout>
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default WelcomeLayout;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createStyles } from 'antd-style';
|
|
2
|
+
import { rgba } from 'polished';
|
|
3
|
+
|
|
4
|
+
export const useStyles = createStyles(({ css, token, stylish, cx }) => {
|
|
5
|
+
return {
|
|
6
|
+
container: css`
|
|
7
|
+
z-index: 10;
|
|
8
|
+
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
align-items: center;
|
|
12
|
+
|
|
13
|
+
width: 100%;
|
|
14
|
+
margin-bottom: 24px;
|
|
15
|
+
`,
|
|
16
|
+
desc: css`
|
|
17
|
+
font-weight: 400;
|
|
18
|
+
color: ${rgba(token.colorText, 0.8)};
|
|
19
|
+
text-align: center;
|
|
20
|
+
`,
|
|
21
|
+
layout: css`
|
|
22
|
+
background: ${token.colorBgContainer};
|
|
23
|
+
`,
|
|
24
|
+
logo: css`
|
|
25
|
+
position: absolute;
|
|
26
|
+
top: 16px;
|
|
27
|
+
left: 16px;
|
|
28
|
+
fill: ${token.colorText};
|
|
29
|
+
`,
|
|
30
|
+
note: css`
|
|
31
|
+
z-index: 10;
|
|
32
|
+
margin-top: 16px;
|
|
33
|
+
color: ${token.colorTextDescription};
|
|
34
|
+
`,
|
|
35
|
+
skip: css`
|
|
36
|
+
color: ${token.colorTextDescription};
|
|
37
|
+
`,
|
|
38
|
+
templateContainer: css`
|
|
39
|
+
flex-wrap: wrap;
|
|
40
|
+
width: 100%;
|
|
41
|
+
padding: 16px;
|
|
42
|
+
`,
|
|
43
|
+
title: css`
|
|
44
|
+
margin-bottom: 0.2em;
|
|
45
|
+
font-weight: 800;
|
|
46
|
+
line-height: 1.2;
|
|
47
|
+
text-align: center;
|
|
48
|
+
`,
|
|
49
|
+
view: cx(
|
|
50
|
+
stylish.noScrollbar,
|
|
51
|
+
css`
|
|
52
|
+
overflow-x: hidden;
|
|
53
|
+
overflow-y: auto;
|
|
54
|
+
height: 100vh;
|
|
55
|
+
padding: 32px 16px;
|
|
56
|
+
`,
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export const genSize = (size: number, minSize: number) => {
|
|
62
|
+
return size < minSize ? minSize : size;
|
|
63
|
+
};
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
currentSessionSafe,
|
|
5
5
|
getSessionById,
|
|
6
6
|
getSessionMetaById,
|
|
7
|
+
hasSessionList,
|
|
7
8
|
sessionList,
|
|
8
9
|
} from './list';
|
|
9
10
|
|
|
@@ -15,5 +16,6 @@ export const sessionSelectors = {
|
|
|
15
16
|
getExportAgent,
|
|
16
17
|
getSessionById,
|
|
17
18
|
getSessionMetaById,
|
|
19
|
+
hasSessionList,
|
|
18
20
|
sessionList,
|
|
19
21
|
};
|
|
@@ -36,6 +36,11 @@ export const sessionList = (s: SessionStore) => {
|
|
|
36
36
|
});
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
export const hasSessionList = (s: SessionStore) => {
|
|
40
|
+
const list = sessionList(s);
|
|
41
|
+
return list?.length > 0;
|
|
42
|
+
};
|
|
43
|
+
|
|
39
44
|
export const getSessionById =
|
|
40
45
|
(id: string) =>
|
|
41
46
|
(s: SessionStore): LobeAgentSession => {
|