@lobehub/chat 0.57.0 → 0.58.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/package.json +1 -1
- package/src/components/SafeSpacing/index.tsx +27 -0
- package/src/const/layoutTokens.ts +2 -0
- package/src/features/AvatarWithUpload/index.tsx +4 -3
- package/src/features/MobileTabBar/index.tsx +60 -0
- package/src/features/SideBar/TopActions.tsx +1 -1
- package/src/layout/AppMobileLayout.tsx +76 -0
- package/src/locales/default/common.ts +3 -1
- package/src/pages/chat/SessionList/Header.tsx +26 -2
- package/src/pages/chat/SessionList/index.tsx +10 -1
- package/src/pages/chat/[id]/index.page.tsx +19 -7
- package/src/pages/chat/[id]/mobile.tsx +31 -0
- package/src/pages/chat/[id]/setting/Header.tsx +25 -2
- package/src/pages/chat/[id]/setting/index.page.tsx +33 -16
- package/src/pages/chat/[id]/setting/mobile.tsx +11 -0
- package/src/pages/chat/features/Conversation/index.tsx +5 -5
- package/src/pages/chat/features/Header/Mobile.tsx +47 -0
- package/src/pages/chat/features/Header/PC.tsx +101 -0
- package/src/pages/chat/features/Header/index.tsx +7 -93
- package/src/pages/chat/features/Sidebar/Header.tsx +6 -4
- package/src/pages/chat/features/Sidebar/Mobile.tsx +36 -0
- package/src/pages/chat/features/Sidebar/SystemRole.tsx +2 -2
- package/src/pages/chat/features/Sidebar/index.tsx +33 -15
- package/src/pages/chat/index.page.tsx +14 -8
- package/src/pages/chat/layout.tsx +12 -8
- package/src/pages/chat/mobile.tsx +25 -0
- package/src/pages/settings/index.page.tsx +40 -8
- package/src/pages/settings/layout.tsx +5 -27
- package/src/pages/settings/mobile.tsx +20 -0
- package/src/pages/welcome/Banner/Hero.tsx +3 -5
- package/src/pages/welcome/Banner/index.tsx +5 -5
- package/src/pages/welcome/index.page.tsx +13 -4
- package/src/pages/welcome/mobile.tsx +20 -0
- package/src/pages/welcome/style.ts +2 -2
- package/src/store/global/initialState.ts +2 -0
- package/src/store/global/slices/common.ts +7 -0
- package/src/components/HeaderSpacing/index.tsx +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.58.0](https://github.com/lobehub/lobe-chat/compare/v0.57.0...v0.58.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-08-26**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Implement responsive design for mobile devices.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Implement responsive design for mobile devices, closes [#95](https://github.com/lobehub/lobe-chat/issues/95) ([fdb3c93](https://github.com/lobehub/lobe-chat/commit/fdb3c93))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 0.57.0](https://github.com/lobehub/lobe-chat/compare/v0.56.0...v0.57.0)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2023-08-26**</sup>
|
package/package.json
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MobileSafeArea, type MobileSafeAreaProps } from '@lobehub/ui';
|
|
2
|
+
import { memo } from 'react';
|
|
3
|
+
|
|
4
|
+
import { HEADER_HEIGHT, MOBILE_NABBAR_HEIGHT, MOBILE_TABBAR_HEIGHT } from '@/const/layoutTokens';
|
|
5
|
+
|
|
6
|
+
interface SafeSpacingProps {
|
|
7
|
+
height?: number;
|
|
8
|
+
mobile?: boolean;
|
|
9
|
+
position?: MobileSafeAreaProps['position'];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const SafeSpacing = memo<SafeSpacingProps>(({ height, position = 'top', mobile }) => {
|
|
13
|
+
let h;
|
|
14
|
+
if (mobile) {
|
|
15
|
+
h = position === 'top' ? MOBILE_NABBAR_HEIGHT : MOBILE_TABBAR_HEIGHT;
|
|
16
|
+
} else {
|
|
17
|
+
h = HEADER_HEIGHT;
|
|
18
|
+
}
|
|
19
|
+
return (
|
|
20
|
+
<>
|
|
21
|
+
<div style={{ flex: 'none', height: height || h }} />
|
|
22
|
+
{mobile && <MobileSafeArea position={position} />}
|
|
23
|
+
</>
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default SafeSpacing;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { FormProps } from '@lobehub/ui';
|
|
2
2
|
|
|
3
3
|
export const HEADER_HEIGHT = 64;
|
|
4
|
+
export const MOBILE_NABBAR_HEIGHT = 44;
|
|
5
|
+
export const MOBILE_TABBAR_HEIGHT = 48;
|
|
4
6
|
export const CHAT_TEXTAREA_HEIGHT = 200;
|
|
5
7
|
export const CHAT_SIDEBAR_WIDTH = 280;
|
|
6
8
|
export const FOLDER_WIDTH = 256;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Avatar, Logo } from '@lobehub/ui';
|
|
2
2
|
import { Upload } from 'antd';
|
|
3
3
|
import { createStyles } from 'antd-style';
|
|
4
|
-
import { memo } from 'react';
|
|
4
|
+
import { CSSProperties, memo } from 'react';
|
|
5
5
|
|
|
6
6
|
import { useGlobalStore } from '@/store/global';
|
|
7
7
|
import { createUploadImageHandler } from '@/utils/uploadFIle';
|
|
@@ -25,9 +25,10 @@ const useStyle = createStyles(
|
|
|
25
25
|
interface AvatarWithUploadProps {
|
|
26
26
|
compressSize?: number;
|
|
27
27
|
size?: number;
|
|
28
|
+
style?: CSSProperties;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const AvatarWithUpload = memo<AvatarWithUploadProps>(({ size = 40, compressSize = 128 }) => {
|
|
31
|
+
const AvatarWithUpload = memo<AvatarWithUploadProps>(({ size = 40, compressSize = 128, style }) => {
|
|
31
32
|
const [avatar, setSettings] = useGlobalStore((st) => [st.settings.avatar, st.setSettings]);
|
|
32
33
|
const { styles } = useStyle();
|
|
33
34
|
|
|
@@ -67,7 +68,7 @@ const AvatarWithUpload = memo<AvatarWithUploadProps>(({ size = 40, compressSize
|
|
|
67
68
|
});
|
|
68
69
|
|
|
69
70
|
return (
|
|
70
|
-
<div className={styles} style={{ maxHeight: size, maxWidth: size }}>
|
|
71
|
+
<div className={styles} style={{ maxHeight: size, maxWidth: size, ...style }}>
|
|
71
72
|
<Upload beforeUpload={handleUploadAvatar} itemRender={() => void 0} maxCount={1}>
|
|
72
73
|
{avatar ? <Avatar avatar={avatar} size={size} /> : <Logo size={size} />}
|
|
73
74
|
</Upload>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
|
|
2
|
+
import { createStyles } from 'antd-style';
|
|
3
|
+
import { MessageSquare, Settings2, Sticker } from 'lucide-react';
|
|
4
|
+
import Router from 'next/router';
|
|
5
|
+
import { rgba } from 'polished';
|
|
6
|
+
import { memo, useMemo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
import { useGlobalStore } from '@/store/global';
|
|
10
|
+
|
|
11
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
12
|
+
active: css`
|
|
13
|
+
svg {
|
|
14
|
+
fill: ${rgba(token.colorPrimary, 0.25)};
|
|
15
|
+
}
|
|
16
|
+
`,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
export default memo<{ className?: string }>(({ className }) => {
|
|
20
|
+
const [tab, setTab] = useGlobalStore((s) => [s.sidebarKey, s.switchSideBar]);
|
|
21
|
+
const { t } = useTranslation('common');
|
|
22
|
+
const { styles } = useStyles();
|
|
23
|
+
const items: MobileTabBarProps['items'] = useMemo(
|
|
24
|
+
() => [
|
|
25
|
+
{
|
|
26
|
+
icon: (active) => (
|
|
27
|
+
<Icon className={active ? styles.active : undefined} icon={MessageSquare} />
|
|
28
|
+
),
|
|
29
|
+
key: 'chat',
|
|
30
|
+
onClick: () => {
|
|
31
|
+
Router.push('/chat');
|
|
32
|
+
},
|
|
33
|
+
title: t('tab.chat'),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
icon: (active) => <Icon className={active ? styles.active : undefined} icon={Sticker} />,
|
|
37
|
+
key: 'market',
|
|
38
|
+
|
|
39
|
+
title: t('tab.market'),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
icon: (active) => <Icon className={active ? styles.active : undefined} icon={Settings2} />,
|
|
43
|
+
key: 'settings',
|
|
44
|
+
onClick: () => {
|
|
45
|
+
Router.push('/settings');
|
|
46
|
+
},
|
|
47
|
+
title: t('tab.setting'),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
[t],
|
|
51
|
+
);
|
|
52
|
+
return (
|
|
53
|
+
<MobileTabBar
|
|
54
|
+
activeKey={tab}
|
|
55
|
+
className={className}
|
|
56
|
+
items={items}
|
|
57
|
+
onChange={(key) => setTab(key as any)}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
});
|
|
@@ -22,8 +22,8 @@ const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
|
|
|
22
22
|
onClick={() => {
|
|
23
23
|
// 如果已经在 chat 路径下了,那么就不用再跳转了
|
|
24
24
|
if (Router.asPath.startsWith('/chat')) return;
|
|
25
|
-
|
|
26
25
|
Router.push('/chat');
|
|
26
|
+
setTab('chat');
|
|
27
27
|
}}
|
|
28
28
|
size="large"
|
|
29
29
|
title={t('tab.chat')}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type MobileNavBarTitleProps } from '@lobehub/ui';
|
|
2
|
+
import { createStyles } from 'antd-style';
|
|
3
|
+
import Head from 'next/head';
|
|
4
|
+
import { CSSProperties, PropsWithChildren, ReactNode, memo } from 'react';
|
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
|
6
|
+
|
|
7
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
8
|
+
import MobileTabBar from '@/features/MobileTabBar';
|
|
9
|
+
|
|
10
|
+
const useStyles = createStyles(({ css }) => ({
|
|
11
|
+
container: css`
|
|
12
|
+
position: relative;
|
|
13
|
+
|
|
14
|
+
overflow-x: hidden;
|
|
15
|
+
overflow-y: auto;
|
|
16
|
+
|
|
17
|
+
width: 100vw;
|
|
18
|
+
height: 100vh;
|
|
19
|
+
`,
|
|
20
|
+
mobileNavBar: css`
|
|
21
|
+
position: fixed;
|
|
22
|
+
z-index: 100;
|
|
23
|
+
top: 0;
|
|
24
|
+
right: 0;
|
|
25
|
+
left: 0;
|
|
26
|
+
`,
|
|
27
|
+
mobileTabBar: css`
|
|
28
|
+
position: fixed;
|
|
29
|
+
z-index: 100;
|
|
30
|
+
right: 0;
|
|
31
|
+
bottom: 0;
|
|
32
|
+
left: 0;
|
|
33
|
+
`,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
interface AppMobileLayoutProps extends PropsWithChildren {
|
|
37
|
+
className?: string;
|
|
38
|
+
navBar?: ReactNode;
|
|
39
|
+
showTabBar?: boolean;
|
|
40
|
+
style?: CSSProperties;
|
|
41
|
+
title?: MobileNavBarTitleProps;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const AppMobileLayout = memo<AppMobileLayoutProps>(
|
|
45
|
+
({ children, showTabBar, navBar, style, className }) => {
|
|
46
|
+
const { styles, cx } = useStyles();
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<Head>
|
|
51
|
+
<meta
|
|
52
|
+
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
|
53
|
+
name="viewport"
|
|
54
|
+
/>
|
|
55
|
+
</Head>
|
|
56
|
+
<Flexbox className={cx(styles.container, className)} style={style}>
|
|
57
|
+
{navBar && (
|
|
58
|
+
<>
|
|
59
|
+
<div className={styles.mobileNavBar}>{navBar}</div>
|
|
60
|
+
<SafeSpacing mobile position={'top'} />
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
{children}
|
|
64
|
+
{showTabBar && (
|
|
65
|
+
<>
|
|
66
|
+
<SafeSpacing mobile position={'bottom'} />
|
|
67
|
+
<MobileTabBar className={styles.mobileTabBar} />
|
|
68
|
+
</>
|
|
69
|
+
)}
|
|
70
|
+
</Flexbox>
|
|
71
|
+
</>
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
export default AppMobileLayout;
|
|
@@ -63,7 +63,8 @@ export default {
|
|
|
63
63
|
stop: '停止',
|
|
64
64
|
tab: {
|
|
65
65
|
chat: '会话',
|
|
66
|
-
market: '
|
|
66
|
+
market: '发现',
|
|
67
|
+
setting: '设置',
|
|
67
68
|
},
|
|
68
69
|
temp: '临时',
|
|
69
70
|
tokenDetail: '角色设定: {{systemRoleToken}} · 历史消息: {{chatsToken}}',
|
|
@@ -77,6 +78,7 @@ export default {
|
|
|
77
78
|
defaultTitle: '默认话题',
|
|
78
79
|
saveCurrentMessages: '将当前会话保存为话题',
|
|
79
80
|
searchPlaceholder: '搜索话题...',
|
|
81
|
+
title: '话题',
|
|
80
82
|
},
|
|
81
83
|
updateAgent: '更新助理信息',
|
|
82
84
|
updatePrompt: '更新提示词',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionIcon, Logo, SearchBar } from '@lobehub/ui';
|
|
1
|
+
import { ActionIcon, Logo, MobileNavBar, SearchBar } from '@lobehub/ui';
|
|
2
2
|
import { createStyles } from 'antd-style';
|
|
3
3
|
import { MessageSquarePlus } from 'lucide-react';
|
|
4
4
|
import Link from 'next/link';
|
|
@@ -6,6 +6,7 @@ import { memo } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
|
8
8
|
|
|
9
|
+
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
9
10
|
import { useSessionStore } from '@/store/session';
|
|
10
11
|
|
|
11
12
|
export const useStyles = createStyles(({ css, token }) => ({
|
|
@@ -18,11 +19,34 @@ export const useStyles = createStyles(({ css, token }) => ({
|
|
|
18
19
|
`,
|
|
19
20
|
}));
|
|
20
21
|
|
|
21
|
-
const Header = memo(() => {
|
|
22
|
+
const Header = memo<{ mobile?: 'navbar' | 'search' }>(({ mobile }) => {
|
|
22
23
|
const { styles } = useStyles();
|
|
23
24
|
const { t } = useTranslation('common');
|
|
24
25
|
const [keywords, createSession] = useSessionStore((s) => [s.searchKeywords, s.createSession]);
|
|
25
26
|
|
|
27
|
+
if (mobile === 'navbar') {
|
|
28
|
+
return (
|
|
29
|
+
<MobileNavBar
|
|
30
|
+
center={<Logo type={'text'} />}
|
|
31
|
+
left={<AvatarWithUpload size={28} style={{ marginLeft: 8 }} />}
|
|
32
|
+
right={<ActionIcon icon={MessageSquarePlus} onClick={createSession} />}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (mobile === 'search')
|
|
38
|
+
return (
|
|
39
|
+
<Flexbox className={styles.top} padding={16}>
|
|
40
|
+
<SearchBar
|
|
41
|
+
allowClear
|
|
42
|
+
onChange={(e) => useSessionStore.setState({ searchKeywords: e.target.value })}
|
|
43
|
+
placeholder={t('searchAgentPlaceholder')}
|
|
44
|
+
type={'block'}
|
|
45
|
+
value={keywords}
|
|
46
|
+
/>
|
|
47
|
+
</Flexbox>
|
|
48
|
+
);
|
|
49
|
+
|
|
26
50
|
return (
|
|
27
51
|
<Flexbox className={styles.top} gap={16} padding={16}>
|
|
28
52
|
<Flexbox distribution={'space-between'} horizontal>
|
|
@@ -13,7 +13,7 @@ import SessionList from './List';
|
|
|
13
13
|
|
|
14
14
|
const useStyles = createStyles(({ stylish }) => stylish.noScrollbar);
|
|
15
15
|
|
|
16
|
-
export const Sessions = memo(() => {
|
|
16
|
+
export const Sessions = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
17
17
|
const { t } = useTranslation('common');
|
|
18
18
|
const { styles } = useStyles();
|
|
19
19
|
|
|
@@ -28,6 +28,15 @@ export const Sessions = memo(() => {
|
|
|
28
28
|
[],
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
+
if (mobile)
|
|
32
|
+
return (
|
|
33
|
+
<>
|
|
34
|
+
<Header mobile={'search'} />
|
|
35
|
+
<Inbox />
|
|
36
|
+
<CollapseGroup defaultActiveKey={['sessionList']} items={items} />
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
|
|
31
40
|
return (
|
|
32
41
|
<FolderPanel>
|
|
33
42
|
<Header />
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useResponsive } from 'antd-style';
|
|
1
2
|
import Head from 'next/head';
|
|
2
3
|
import { memo } from 'react';
|
|
3
4
|
import { Flexbox } from 'react-layout-kit';
|
|
@@ -9,8 +10,10 @@ import Conversation from '../features/Conversation';
|
|
|
9
10
|
import Header from '../features/Header';
|
|
10
11
|
import SideBar from '../features/Sidebar';
|
|
11
12
|
import Layout from './layout';
|
|
13
|
+
import Mobile from './mobile';
|
|
12
14
|
|
|
13
15
|
const Chat = memo(() => {
|
|
16
|
+
const { mobile } = useResponsive();
|
|
14
17
|
const [avatar, title] = useSessionStore((s) => [
|
|
15
18
|
agentSelectors.currentAgentAvatar(s),
|
|
16
19
|
agentSelectors.currentAgentTitle(s),
|
|
@@ -23,13 +26,22 @@ const Chat = memo(() => {
|
|
|
23
26
|
<Head>
|
|
24
27
|
<title>{pageTitle}</title>
|
|
25
28
|
</Head>
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
{mobile ? (
|
|
30
|
+
<Mobile>
|
|
31
|
+
<Flexbox height={'calc(100vh - 44px)'} horizontal>
|
|
32
|
+
<Conversation mobile />
|
|
33
|
+
<SideBar mobile />
|
|
34
|
+
</Flexbox>
|
|
35
|
+
</Mobile>
|
|
36
|
+
) : (
|
|
37
|
+
<Layout>
|
|
38
|
+
<Header />
|
|
39
|
+
<Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
|
|
40
|
+
<Conversation />
|
|
41
|
+
<SideBar />
|
|
42
|
+
</Flexbox>
|
|
43
|
+
</Layout>
|
|
44
|
+
)}
|
|
33
45
|
</>
|
|
34
46
|
);
|
|
35
47
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useRouter } from 'next/router';
|
|
2
|
+
import { PropsWithChildren, memo } from 'react';
|
|
3
|
+
import { shallow } from 'zustand/shallow';
|
|
4
|
+
|
|
5
|
+
import AppMobileLayout from '@/layout/AppMobileLayout';
|
|
6
|
+
import { useOnFinishHydrationSession, useSessionStore } from '@/store/session';
|
|
7
|
+
|
|
8
|
+
import Header from '../features/Header';
|
|
9
|
+
|
|
10
|
+
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
11
|
+
const [activeSession, toggleTopic] = useSessionStore((s) => {
|
|
12
|
+
return [s.activeSession, s.toggleTopic];
|
|
13
|
+
}, shallow);
|
|
14
|
+
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
const { id } = router.query;
|
|
17
|
+
|
|
18
|
+
useOnFinishHydrationSession(() => {
|
|
19
|
+
// 1. 正常激活会话
|
|
20
|
+
if (typeof id === 'string') {
|
|
21
|
+
activeSession(id);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 将话题重置为默认值
|
|
25
|
+
toggleTopic();
|
|
26
|
+
}, [id]);
|
|
27
|
+
|
|
28
|
+
return <AppMobileLayout navBar={<Header />}>{children}</AppMobileLayout>;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default MobileLayout;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ActionIcon,
|
|
3
|
+
ChatHeader,
|
|
4
|
+
ChatHeaderTitle,
|
|
5
|
+
MobileNavBar,
|
|
6
|
+
MobileNavBarTitle,
|
|
7
|
+
} from '@lobehub/ui';
|
|
2
8
|
import { Dropdown, MenuProps } from 'antd';
|
|
3
9
|
import { HardDriveDownload, Share2 } from 'lucide-react';
|
|
4
10
|
import Router from 'next/router';
|
|
@@ -8,7 +14,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
8
14
|
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
|
9
15
|
import { useSessionStore } from '@/store/session';
|
|
10
16
|
|
|
11
|
-
const Header = memo(() => {
|
|
17
|
+
const Header = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
12
18
|
const { t } = useTranslation('setting');
|
|
13
19
|
const id = useSessionStore((s) => s.activeId);
|
|
14
20
|
|
|
@@ -36,6 +42,23 @@ const Header = memo(() => {
|
|
|
36
42
|
[],
|
|
37
43
|
);
|
|
38
44
|
|
|
45
|
+
if (mobile)
|
|
46
|
+
return (
|
|
47
|
+
<MobileNavBar
|
|
48
|
+
center={<MobileNavBarTitle title={t('header.session')} />}
|
|
49
|
+
onBackClick={() => Router.back()}
|
|
50
|
+
right={
|
|
51
|
+
<>
|
|
52
|
+
<ActionIcon icon={Share2} title={t('share', { ns: 'common' })} />
|
|
53
|
+
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
|
54
|
+
<ActionIcon icon={HardDriveDownload} title={t('export', { ns: 'common' })} />
|
|
55
|
+
</Dropdown>
|
|
56
|
+
</>
|
|
57
|
+
}
|
|
58
|
+
showBackButton
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
|
|
39
62
|
return (
|
|
40
63
|
<ChatHeader
|
|
41
64
|
left={<ChatHeaderTitle title={t('header.session')} />}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { useResponsive } from 'antd-style';
|
|
1
2
|
import isEqual from 'fast-deep-equal';
|
|
2
3
|
import Head from 'next/head';
|
|
3
4
|
import { memo } from 'react';
|
|
4
5
|
import { useTranslation } from 'react-i18next';
|
|
5
6
|
import { Flexbox } from 'react-layout-kit';
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
8
9
|
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
|
9
10
|
import { AgentConfig, AgentMeta, AgentPlugin, AgentPrompt } from '@/features/AgentSetting';
|
|
10
11
|
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
@@ -12,8 +13,10 @@ import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
|
|
|
12
13
|
|
|
13
14
|
import ChatLayout from '../layout';
|
|
14
15
|
import Header from './Header';
|
|
16
|
+
import Mobile from './mobile';
|
|
15
17
|
|
|
16
18
|
const EditPage = memo(() => {
|
|
19
|
+
const { mobile } = useResponsive();
|
|
17
20
|
const { t } = useTranslation('setting');
|
|
18
21
|
const config = useSessionStore(agentSelectors.currentAgentConfig, isEqual);
|
|
19
22
|
const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
|
@@ -27,26 +30,40 @@ const EditPage = memo(() => {
|
|
|
27
30
|
|
|
28
31
|
const pageTitle = genSiteHeadTitle(t('header.sessionWithName', { name: title }));
|
|
29
32
|
|
|
33
|
+
const settings = (
|
|
34
|
+
<>
|
|
35
|
+
<AgentPrompt config={config} updateConfig={updateAgentConfig} />
|
|
36
|
+
<AgentMeta
|
|
37
|
+
autocomplete={autocomplete}
|
|
38
|
+
config={config}
|
|
39
|
+
meta={meta}
|
|
40
|
+
updateMeta={updateAgentMeta}
|
|
41
|
+
/>
|
|
42
|
+
<AgentConfig config={config} updateConfig={updateAgentConfig} />
|
|
43
|
+
<AgentPlugin config={config} updateConfig={toggleAgentPlugin} />
|
|
44
|
+
</>
|
|
45
|
+
);
|
|
46
|
+
|
|
30
47
|
return (
|
|
31
48
|
<>
|
|
32
49
|
<Head>
|
|
33
50
|
<title>{pageTitle}</title>
|
|
34
51
|
</Head>
|
|
35
|
-
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
</
|
|
49
|
-
|
|
52
|
+
{mobile ? (
|
|
53
|
+
<Mobile>
|
|
54
|
+
<Flexbox gap={16} padding={16}>
|
|
55
|
+
{settings}
|
|
56
|
+
</Flexbox>
|
|
57
|
+
</Mobile>
|
|
58
|
+
) : (
|
|
59
|
+
<ChatLayout>
|
|
60
|
+
<Header />
|
|
61
|
+
<Flexbox align={'center'} flex={1} gap={16} padding={24} style={{ overflow: 'auto' }}>
|
|
62
|
+
<SafeSpacing height={HEADER_HEIGHT - 16} />
|
|
63
|
+
{settings}
|
|
64
|
+
</Flexbox>
|
|
65
|
+
</ChatLayout>
|
|
66
|
+
)}
|
|
50
67
|
</>
|
|
51
68
|
);
|
|
52
69
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PropsWithChildren, memo } from 'react';
|
|
2
|
+
|
|
3
|
+
import AppMobileLayout from '@/layout/AppMobileLayout';
|
|
4
|
+
|
|
5
|
+
import Header from './Header';
|
|
6
|
+
|
|
7
|
+
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
8
|
+
return <AppMobileLayout navBar={<Header mobile />}>{children}</AppMobileLayout>;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export default MobileLayout;
|
|
@@ -3,19 +3,19 @@ import { memo, useRef } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { Flexbox } from 'react-layout-kit';
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
7
7
|
|
|
8
8
|
import ChatList from './ChatList';
|
|
9
9
|
import ChatInput from './Input';
|
|
10
10
|
|
|
11
|
-
const Conversation = () => {
|
|
11
|
+
const Conversation = memo<{ mobile?: boolean }>(({ mobile }) => {
|
|
12
12
|
const ref = useRef(null);
|
|
13
13
|
const { t } = useTranslation('common');
|
|
14
14
|
return (
|
|
15
15
|
<Flexbox flex={1} style={{ position: 'relative' }}>
|
|
16
16
|
<div style={{ flex: 1, overflow: 'hidden', position: 'relative' }}>
|
|
17
17
|
<div ref={ref} style={{ height: '100%', overflowY: 'scroll' }}>
|
|
18
|
-
<
|
|
18
|
+
{!mobile && <SafeSpacing />}
|
|
19
19
|
<ChatList />
|
|
20
20
|
</div>
|
|
21
21
|
<BackBottom target={ref} text={t('backToBottom')} />
|
|
@@ -23,6 +23,6 @@ const Conversation = () => {
|
|
|
23
23
|
<ChatInput />
|
|
24
24
|
</Flexbox>
|
|
25
25
|
);
|
|
26
|
-
};
|
|
26
|
+
});
|
|
27
27
|
|
|
28
|
-
export default
|
|
28
|
+
export default Conversation;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ActionIcon, MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
2
|
+
import { LayoutList, Settings } from 'lucide-react';
|
|
3
|
+
import Router from 'next/router';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
|
8
|
+
import { useGlobalStore } from '@/store/global';
|
|
9
|
+
import { agentSelectors, useSessionStore } from '@/store/session';
|
|
10
|
+
|
|
11
|
+
const MobileHeader = memo<{ settings?: boolean }>(({ settings = true }) => {
|
|
12
|
+
const { t } = useTranslation('common');
|
|
13
|
+
|
|
14
|
+
const [id, title, model] = useSessionStore((s) => [
|
|
15
|
+
s.activeId,
|
|
16
|
+
agentSelectors.currentAgentTitle(s),
|
|
17
|
+
agentSelectors.currentAgentModel(s),
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const isInbox = id === INBOX_SESSION_ID;
|
|
21
|
+
const [toggleConfig] = useGlobalStore((s) => [s.toggleMobileTopic]);
|
|
22
|
+
|
|
23
|
+
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<MobileNavBar
|
|
27
|
+
center={<MobileNavBarTitle desc={model} title={displayTitle} />}
|
|
28
|
+
onBackClick={() => Router.back()}
|
|
29
|
+
right={
|
|
30
|
+
<>
|
|
31
|
+
<ActionIcon icon={LayoutList} onClick={() => toggleConfig()} />
|
|
32
|
+
{settings && (
|
|
33
|
+
<ActionIcon
|
|
34
|
+
icon={Settings}
|
|
35
|
+
onClick={() => {
|
|
36
|
+
Router.push(`/chat/${id}/setting`);
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
39
|
+
)}
|
|
40
|
+
</>
|
|
41
|
+
}
|
|
42
|
+
showBackButton
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export default MobileHeader;
|