@lobehub/chat 0.57.0 → 0.59.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 +51 -0
- package/package.json +1 -1
- package/src/components/SafeSpacing/index.tsx +27 -0
- package/src/const/layoutTokens.ts +2 -0
- package/src/features/AgentSetting/AgentPlugin/index.tsx +4 -4
- 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 +4 -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/ChatList/Error/Plugin/PluginSettings.tsx +7 -5
- package/src/pages/chat/features/Conversation/ChatList/Plugins/CustomRender.tsx +6 -4
- 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/PluginTag.tsx +40 -14
- 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/store/plugin/helpers.ts +26 -0
- package/src/store/plugin/index.ts +1 -0
- package/src/store/plugin/selectors.ts +30 -4
- package/src/components/HeaderSpacing/index.tsx +0 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PluginRender, PluginRenderProps } from '@lobehub/chat-plugin-sdk';
|
|
2
2
|
import { Skeleton } from 'antd';
|
|
3
|
-
import { memo, useState } from 'react';
|
|
3
|
+
import { memo, useEffect, useState } from 'react';
|
|
4
4
|
|
|
5
5
|
import { system } from './dynamticLoader';
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ interface CustomRenderProps extends PluginRenderProps {
|
|
|
10
10
|
const CustomRender = memo<CustomRenderProps>(({ url, ...props }) => {
|
|
11
11
|
const [component, setComp] = useState<PluginRender | null>(null);
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
useEffect(() => {
|
|
14
14
|
system
|
|
15
15
|
.import(url)
|
|
16
16
|
.then((module1) => {
|
|
@@ -21,12 +21,14 @@ const CustomRender = memo<CustomRenderProps>(({ url, ...props }) => {
|
|
|
21
21
|
setComp(null);
|
|
22
22
|
console.error(error);
|
|
23
23
|
});
|
|
24
|
+
}, [url]);
|
|
24
25
|
|
|
26
|
+
if (!component) {
|
|
25
27
|
return <Skeleton active style={{ width: 300 }} />;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
const
|
|
30
|
+
const Render = component;
|
|
29
31
|
|
|
30
|
-
return <
|
|
32
|
+
return <Render {...props} />;
|
|
31
33
|
});
|
|
32
34
|
export default CustomRender;
|
|
@@ -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;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { SiOpenai } from '@icons-pack/react-simple-icons';
|
|
2
|
+
import { ActionIcon, Avatar, ChatHeader, ChatHeaderTitle, Tag } from '@lobehub/ui';
|
|
3
|
+
import { Skeleton } from 'antd';
|
|
4
|
+
import { PanelRightClose, PanelRightOpen, Settings } from 'lucide-react';
|
|
5
|
+
import Router from 'next/router';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
import { INBOX_SESSION_ID } from '@/const/session';
|
|
11
|
+
import { useGlobalStore } from '@/store/global';
|
|
12
|
+
import { agentSelectors, useSessionChatInit, useSessionStore } from '@/store/session';
|
|
13
|
+
|
|
14
|
+
import PluginTag from './PluginTag';
|
|
15
|
+
|
|
16
|
+
const Header = memo<{ settings?: boolean }>(({ settings = true }) => {
|
|
17
|
+
const init = useSessionChatInit();
|
|
18
|
+
|
|
19
|
+
const { t } = useTranslation('common');
|
|
20
|
+
|
|
21
|
+
const [id, title, description, avatar, backgroundColor, model, plugins] = useSessionStore((s) => [
|
|
22
|
+
s.activeId,
|
|
23
|
+
|
|
24
|
+
agentSelectors.currentAgentTitle(s),
|
|
25
|
+
agentSelectors.currentAgentDescription(s),
|
|
26
|
+
agentSelectors.currentAgentAvatar(s),
|
|
27
|
+
agentSelectors.currentAgentBackgroundColor(s),
|
|
28
|
+
agentSelectors.currentAgentModel(s),
|
|
29
|
+
agentSelectors.currentAgentPlugins(s),
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const isInbox = id === INBOX_SESSION_ID;
|
|
33
|
+
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
34
|
+
s.preference.showChatSideBar,
|
|
35
|
+
s.toggleChatSideBar,
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
39
|
+
const displayDesc = isInbox ? t('inbox.desc') : description;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<ChatHeader
|
|
43
|
+
left={
|
|
44
|
+
!init ? (
|
|
45
|
+
<Flexbox horizontal>
|
|
46
|
+
<Skeleton
|
|
47
|
+
active
|
|
48
|
+
avatar={{ shape: 'circle', size: 'default' }}
|
|
49
|
+
paragraph={false}
|
|
50
|
+
title={{ style: { margin: 0, marginTop: 8 }, width: 200 }}
|
|
51
|
+
/>
|
|
52
|
+
</Flexbox>
|
|
53
|
+
) : (
|
|
54
|
+
<Flexbox align={'flex-start'} gap={12} horizontal>
|
|
55
|
+
<Avatar avatar={avatar} background={backgroundColor} size={40} title={title} />
|
|
56
|
+
<ChatHeaderTitle
|
|
57
|
+
desc={displayDesc}
|
|
58
|
+
tag={
|
|
59
|
+
<>
|
|
60
|
+
<Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>
|
|
61
|
+
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
|
|
62
|
+
</>
|
|
63
|
+
}
|
|
64
|
+
title={displayTitle}
|
|
65
|
+
/>
|
|
66
|
+
</Flexbox>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
right={
|
|
70
|
+
<>
|
|
71
|
+
{/*<ActionIcon*/}
|
|
72
|
+
{/* icon={Share2}*/}
|
|
73
|
+
{/* onClick={() => {*/}
|
|
74
|
+
{/* // genShareUrl();*/}
|
|
75
|
+
{/* }}*/}
|
|
76
|
+
{/* size={{ fontSize: 24 }}*/}
|
|
77
|
+
{/* title={t('share')}*/}
|
|
78
|
+
{/*/>*/}
|
|
79
|
+
<ActionIcon
|
|
80
|
+
icon={showAgentSettings ? PanelRightClose : PanelRightOpen}
|
|
81
|
+
onClick={() => toggleConfig()}
|
|
82
|
+
size={{ fontSize: 24 }}
|
|
83
|
+
title={t('roleAndArchive')}
|
|
84
|
+
/>
|
|
85
|
+
{settings && (
|
|
86
|
+
<ActionIcon
|
|
87
|
+
icon={Settings}
|
|
88
|
+
onClick={() => {
|
|
89
|
+
Router.push(`/chat/${id}/setting`);
|
|
90
|
+
}}
|
|
91
|
+
size={{ fontSize: 24 }}
|
|
92
|
+
title={t('header.session', { ns: 'setting' })}
|
|
93
|
+
/>
|
|
94
|
+
)}
|
|
95
|
+
</>
|
|
96
|
+
}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export default Header;
|
|
@@ -1,33 +1,59 @@
|
|
|
1
|
-
import { Avatar, Icon, Tag } from '@lobehub/ui';
|
|
1
|
+
import { ActionIcon, Avatar, Icon, Tag } from '@lobehub/ui';
|
|
2
2
|
import type { MenuProps } from 'antd';
|
|
3
|
-
import { Dropdown } from 'antd';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
3
|
+
import { Badge, Dropdown } from 'antd';
|
|
4
|
+
import isEqual from 'fast-deep-equal';
|
|
5
|
+
import { LucideRotateCw, LucideToyBrick } from 'lucide-react';
|
|
6
|
+
import { memo, useMemo } from 'react';
|
|
6
7
|
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
9
|
|
|
8
|
-
import { usePluginStore } from '@/store/plugin';
|
|
10
|
+
import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
11
|
+
|
|
12
|
+
const PluginStatus = memo<{ id: string; title?: string }>(({ title, id }) => {
|
|
13
|
+
const { t } = useTranslation('common');
|
|
14
|
+
const status = usePluginStore(pluginSelectors.getPluginManifestLoadingStatus(id));
|
|
15
|
+
|
|
16
|
+
const renderStatus = useMemo(() => {
|
|
17
|
+
switch (status) {
|
|
18
|
+
case 'loading': {
|
|
19
|
+
return <Badge color={'blue'} status={'processing'} />;
|
|
20
|
+
}
|
|
21
|
+
case 'error': {
|
|
22
|
+
return <ActionIcon icon={LucideRotateCw} size={'small'} title={t('retry')} />;
|
|
23
|
+
}
|
|
24
|
+
case 'success': {
|
|
25
|
+
return <Badge status={status} />;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}, [status]);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Flexbox gap={12} horizontal justify={'space-between'}>
|
|
32
|
+
{title} {renderStatus}
|
|
33
|
+
</Flexbox>
|
|
34
|
+
);
|
|
35
|
+
});
|
|
9
36
|
|
|
10
37
|
export interface PluginTagProps {
|
|
11
38
|
plugins: string[];
|
|
12
39
|
}
|
|
13
40
|
|
|
14
41
|
const PluginTag = memo<PluginTagProps>(({ plugins }) => {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const list = usePluginStore((s) => s.pluginList);
|
|
42
|
+
const list = usePluginStore(pluginSelectors.displayPluginList);
|
|
43
|
+
const displayPlugin = usePluginStore(pluginSelectors.getPluginMetaById(plugins[0]), isEqual);
|
|
18
44
|
|
|
19
45
|
if (plugins.length === 0) return null;
|
|
20
46
|
|
|
21
47
|
const items: MenuProps['items'] = plugins.map((id) => {
|
|
22
|
-
const item = list
|
|
48
|
+
const item = list.find((i) => i.identifier === id);
|
|
49
|
+
|
|
23
50
|
return {
|
|
24
|
-
icon:
|
|
25
|
-
<Avatar avatar={item?.meta.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />
|
|
26
|
-
),
|
|
51
|
+
icon: <Avatar avatar={item?.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />,
|
|
27
52
|
key: id,
|
|
28
|
-
label:
|
|
53
|
+
label: <PluginStatus id={id} title={item?.title} />,
|
|
29
54
|
};
|
|
30
55
|
});
|
|
56
|
+
|
|
31
57
|
const count = plugins.length;
|
|
32
58
|
|
|
33
59
|
return (
|
|
@@ -35,7 +61,7 @@ const PluginTag = memo<PluginTagProps>(({ plugins }) => {
|
|
|
35
61
|
<div>
|
|
36
62
|
<Tag>
|
|
37
63
|
{<Icon icon={LucideToyBrick} />}
|
|
38
|
-
{
|
|
64
|
+
{pluginHelpers.getPluginTitle(displayPlugin?.meta)}
|
|
39
65
|
{count > 1 && <div>({plugins.length - 1}+)</div>}
|
|
40
66
|
</Tag>
|
|
41
67
|
</div>
|
|
@@ -1,101 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ActionIcon, Avatar, ChatHeader, ChatHeaderTitle, Tag } from '@lobehub/ui';
|
|
3
|
-
import { Skeleton } from 'antd';
|
|
4
|
-
import { PanelRightClose, PanelRightOpen, Settings } from 'lucide-react';
|
|
5
|
-
import Router from 'next/router';
|
|
1
|
+
import { useResponsive } from 'antd-style';
|
|
6
2
|
import { memo } from 'react';
|
|
7
|
-
import { useTranslation } from 'react-i18next';
|
|
8
|
-
import { Flexbox } from 'react-layout-kit';
|
|
9
3
|
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import { agentSelectors, useSessionChatInit, useSessionStore } from '@/store/session';
|
|
4
|
+
import Mobile from './Mobile';
|
|
5
|
+
import PC from './PC';
|
|
13
6
|
|
|
14
|
-
|
|
7
|
+
const Header = memo<{ settings?: boolean }>((props) => {
|
|
8
|
+
const { mobile } = useResponsive();
|
|
15
9
|
|
|
16
|
-
|
|
17
|
-
const init = useSessionChatInit();
|
|
10
|
+
if (mobile) return <Mobile {...props} />;
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const [id, title, description, avatar, backgroundColor, model, plugins] = useSessionStore((s) => [
|
|
22
|
-
s.activeId,
|
|
23
|
-
|
|
24
|
-
agentSelectors.currentAgentTitle(s),
|
|
25
|
-
agentSelectors.currentAgentDescription(s),
|
|
26
|
-
agentSelectors.currentAgentAvatar(s),
|
|
27
|
-
agentSelectors.currentAgentBackgroundColor(s),
|
|
28
|
-
agentSelectors.currentAgentModel(s),
|
|
29
|
-
agentSelectors.currentAgentPlugins(s),
|
|
30
|
-
]);
|
|
31
|
-
|
|
32
|
-
const isInbox = id === INBOX_SESSION_ID;
|
|
33
|
-
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
34
|
-
s.preference.showChatSideBar,
|
|
35
|
-
s.toggleChatSideBar,
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
39
|
-
const displayDesc = isInbox ? t('inbox.desc') : description;
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<ChatHeader
|
|
43
|
-
left={
|
|
44
|
-
!init ? (
|
|
45
|
-
<Flexbox horizontal>
|
|
46
|
-
<Skeleton
|
|
47
|
-
active
|
|
48
|
-
avatar={{ shape: 'circle', size: 'default' }}
|
|
49
|
-
paragraph={false}
|
|
50
|
-
title={{ style: { margin: 0, marginTop: 8 }, width: 200 }}
|
|
51
|
-
/>
|
|
52
|
-
</Flexbox>
|
|
53
|
-
) : (
|
|
54
|
-
<Flexbox align={'flex-start'} gap={12} horizontal>
|
|
55
|
-
<Avatar avatar={avatar} background={backgroundColor} size={40} title={title} />
|
|
56
|
-
<ChatHeaderTitle
|
|
57
|
-
desc={displayDesc}
|
|
58
|
-
tag={
|
|
59
|
-
<>
|
|
60
|
-
<Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>
|
|
61
|
-
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
|
|
62
|
-
</>
|
|
63
|
-
}
|
|
64
|
-
title={displayTitle}
|
|
65
|
-
/>
|
|
66
|
-
</Flexbox>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
right={
|
|
70
|
-
<>
|
|
71
|
-
{/*<ActionIcon*/}
|
|
72
|
-
{/* icon={Share2}*/}
|
|
73
|
-
{/* onClick={() => {*/}
|
|
74
|
-
{/* // genShareUrl();*/}
|
|
75
|
-
{/* }}*/}
|
|
76
|
-
{/* size={{ fontSize: 24 }}*/}
|
|
77
|
-
{/* title={t('share')}*/}
|
|
78
|
-
{/*/>*/}
|
|
79
|
-
<ActionIcon
|
|
80
|
-
icon={showAgentSettings ? PanelRightClose : PanelRightOpen}
|
|
81
|
-
onClick={() => toggleConfig()}
|
|
82
|
-
size={{ fontSize: 24 }}
|
|
83
|
-
title={t('roleAndArchive')}
|
|
84
|
-
/>
|
|
85
|
-
{settings && (
|
|
86
|
-
<ActionIcon
|
|
87
|
-
icon={Settings}
|
|
88
|
-
onClick={() => {
|
|
89
|
-
Router.push(`/chat/${id}/setting`);
|
|
90
|
-
}}
|
|
91
|
-
size={{ fontSize: 24 }}
|
|
92
|
-
title={t('header.session', { ns: 'setting' })}
|
|
93
|
-
/>
|
|
94
|
-
)}
|
|
95
|
-
</>
|
|
96
|
-
}
|
|
97
|
-
/>
|
|
98
|
-
);
|
|
12
|
+
return <PC {...props} />;
|
|
99
13
|
});
|
|
100
14
|
|
|
101
15
|
export default Header;
|
|
@@ -2,24 +2,26 @@ import { createStyles } from 'antd-style';
|
|
|
2
2
|
import { type ReactNode, memo } from 'react';
|
|
3
3
|
import { Flexbox } from 'react-layout-kit';
|
|
4
4
|
|
|
5
|
-
const useStyles = createStyles(
|
|
6
|
-
|
|
5
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
6
|
+
header: css`
|
|
7
7
|
z-index: 10;
|
|
8
8
|
box-shadow: 0 2px 6px ${token.colorBgLayout};
|
|
9
9
|
`,
|
|
10
|
-
);
|
|
10
|
+
}));
|
|
11
11
|
|
|
12
12
|
interface HeaderProps {
|
|
13
13
|
actions?: ReactNode;
|
|
14
|
+
mobile?: boolean;
|
|
14
15
|
title: string;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
const Header = memo<HeaderProps>(({ title, actions }) => {
|
|
18
19
|
const { styles } = useStyles();
|
|
20
|
+
|
|
19
21
|
return (
|
|
20
22
|
<Flexbox
|
|
21
23
|
align={'center'}
|
|
22
|
-
className={styles}
|
|
24
|
+
className={styles.header}
|
|
23
25
|
distribution={'space-between'}
|
|
24
26
|
horizontal
|
|
25
27
|
padding={12}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ActionIcon } from '@lobehub/ui';
|
|
2
|
+
import { Drawer } from 'antd';
|
|
3
|
+
import { useTheme } from 'antd-style';
|
|
4
|
+
import { X } from 'lucide-react';
|
|
5
|
+
import { PropsWithChildren, memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
import { useGlobalStore } from '@/store/global';
|
|
9
|
+
|
|
10
|
+
const Mobile = memo<PropsWithChildren>(({ children }) => {
|
|
11
|
+
const theme = useTheme();
|
|
12
|
+
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
13
|
+
s.preference.mobileShowTopic,
|
|
14
|
+
s.toggleMobileTopic,
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
const { t } = useTranslation('common');
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Drawer
|
|
21
|
+
bodyStyle={{ padding: 0 }}
|
|
22
|
+
closeIcon={<ActionIcon icon={X} size={{ blockSize: 32, fontSize: 20 }} />}
|
|
23
|
+
drawerStyle={{ background: theme.colorBgContainer }}
|
|
24
|
+
headerStyle={{ padding: '8px 4px' }}
|
|
25
|
+
height={'75vh'}
|
|
26
|
+
onClose={() => toggleConfig(false)}
|
|
27
|
+
open={showAgentSettings}
|
|
28
|
+
placement={'bottom'}
|
|
29
|
+
title={t('topic.title')}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</Drawer>
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export default Mobile;
|
|
@@ -44,7 +44,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
44
44
|
`,
|
|
45
45
|
}));
|
|
46
46
|
|
|
47
|
-
const SystemRole = memo(() => {
|
|
47
|
+
const SystemRole = memo<{ mobile?: boolean }>(() => {
|
|
48
48
|
const [openModal, setOpenModal] = useState(false);
|
|
49
49
|
|
|
50
50
|
const { styles } = useStyles();
|
|
@@ -62,7 +62,7 @@ const SystemRole = memo(() => {
|
|
|
62
62
|
<ActionIcon
|
|
63
63
|
icon={Edit}
|
|
64
64
|
onClick={() => setOpenModal(true)}
|
|
65
|
-
size=
|
|
65
|
+
size={'small'}
|
|
66
66
|
title={t('edit')}
|
|
67
67
|
/>
|
|
68
68
|
}
|
|
@@ -5,20 +5,25 @@ import { memo } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import SafeSpacing from '@/components/SafeSpacing';
|
|
9
9
|
import { CHAT_SIDEBAR_WIDTH } from '@/const/layoutTokens';
|
|
10
10
|
import { useGlobalStore } from '@/store/global';
|
|
11
11
|
|
|
12
|
+
import Mobile from './Mobile';
|
|
12
13
|
import SystemRole from './SystemRole';
|
|
13
14
|
import { Topic } from './Topic';
|
|
14
15
|
|
|
15
|
-
const useStyles = createStyles(({ cx, css, token, stylish }) => ({
|
|
16
|
+
const useStyles = createStyles(({ cx, css, token, stylish }, mobile: boolean) => ({
|
|
16
17
|
content: css`
|
|
17
18
|
display: flex;
|
|
18
19
|
flex-direction: column;
|
|
19
20
|
`,
|
|
20
21
|
drawer: cx(
|
|
21
|
-
|
|
22
|
+
mobile
|
|
23
|
+
? css`
|
|
24
|
+
background: ${token.colorBgLayout} !important;
|
|
25
|
+
`
|
|
26
|
+
: stylish.blurStrong,
|
|
22
27
|
css`
|
|
23
28
|
background: ${rgba(token.colorBgLayout, 0.4)};
|
|
24
29
|
`,
|
|
@@ -29,10 +34,11 @@ const useStyles = createStyles(({ cx, css, token, stylish }) => ({
|
|
|
29
34
|
}));
|
|
30
35
|
|
|
31
36
|
interface SideBarProps {
|
|
37
|
+
mobile?: boolean;
|
|
32
38
|
systemRole?: boolean;
|
|
33
39
|
}
|
|
34
|
-
const SideBar = memo<SideBarProps>(({ systemRole = true }) => {
|
|
35
|
-
const { styles } = useStyles();
|
|
40
|
+
const SideBar = memo<SideBarProps>(({ systemRole = true, mobile }) => {
|
|
41
|
+
const { styles } = useStyles(mobile);
|
|
36
42
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
37
43
|
s.preference.showChatSideBar,
|
|
38
44
|
s.toggleChatSideBar,
|
|
@@ -40,6 +46,23 @@ const SideBar = memo<SideBarProps>(({ systemRole = true }) => {
|
|
|
40
46
|
|
|
41
47
|
const { t } = useTranslation('common');
|
|
42
48
|
|
|
49
|
+
const topic = (
|
|
50
|
+
<Flexbox height={'100%'} style={{ overflow: 'hidden' }}>
|
|
51
|
+
<Flexbox padding={16}>
|
|
52
|
+
<SearchBar
|
|
53
|
+
placeholder={t('topic.searchPlaceholder')}
|
|
54
|
+
spotlight={!mobile}
|
|
55
|
+
type={mobile ? 'block' : 'ghost'}
|
|
56
|
+
/>
|
|
57
|
+
</Flexbox>
|
|
58
|
+
<Flexbox gap={16} paddingInline={16} style={{ overflowY: 'auto', position: 'relative' }}>
|
|
59
|
+
<Topic />
|
|
60
|
+
</Flexbox>
|
|
61
|
+
</Flexbox>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (mobile) return <Mobile>{topic}</Mobile>;
|
|
65
|
+
|
|
43
66
|
return (
|
|
44
67
|
<DraggablePanel
|
|
45
68
|
className={styles.drawer}
|
|
@@ -48,22 +71,17 @@ const SideBar = memo<SideBarProps>(({ systemRole = true }) => {
|
|
|
48
71
|
}}
|
|
49
72
|
expand={showAgentSettings}
|
|
50
73
|
minWidth={CHAT_SIDEBAR_WIDTH}
|
|
74
|
+
mode={'fixed'}
|
|
51
75
|
onExpandChange={toggleConfig}
|
|
52
76
|
placement={'right'}
|
|
53
77
|
>
|
|
54
78
|
<DraggablePanelContainer
|
|
55
79
|
style={{ flex: 'none', height: '100%', minWidth: CHAT_SIDEBAR_WIDTH }}
|
|
56
80
|
>
|
|
57
|
-
<
|
|
58
|
-
{systemRole && <SystemRole />}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<SearchBar placeholder={t('topic.searchPlaceholder')} spotlight type={'ghost'} />
|
|
62
|
-
</Flexbox>
|
|
63
|
-
<Flexbox gap={16} paddingInline={16} style={{ overflowY: 'auto', position: 'relative' }}>
|
|
64
|
-
<Topic />
|
|
65
|
-
</Flexbox>
|
|
66
|
-
</Flexbox>
|
|
81
|
+
<SafeSpacing />
|
|
82
|
+
{systemRole && <SystemRole mobile={mobile} />}
|
|
83
|
+
|
|
84
|
+
{topic}
|
|
67
85
|
</DraggablePanelContainer>
|
|
68
86
|
</DraggablePanel>
|
|
69
87
|
);
|
|
@@ -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 { useTranslation } from 'react-i18next';
|
|
@@ -11,10 +12,11 @@ import Conversation from './features/Conversation';
|
|
|
11
12
|
import Header from './features/Header';
|
|
12
13
|
import SideBar from './features/Sidebar';
|
|
13
14
|
import Layout from './layout';
|
|
15
|
+
import Mobile from './mobile';
|
|
14
16
|
|
|
15
17
|
const Chat = memo(() => {
|
|
16
18
|
const { t } = useTranslation('common');
|
|
17
|
-
|
|
19
|
+
const { mobile } = useResponsive();
|
|
18
20
|
const pageTitle = genSiteHeadTitle(t('inbox.title'));
|
|
19
21
|
|
|
20
22
|
useOnFinishHydrationSession(() => {
|
|
@@ -26,13 +28,17 @@ const Chat = memo(() => {
|
|
|
26
28
|
<Head>
|
|
27
29
|
<title>{pageTitle}</title>
|
|
28
30
|
</Head>
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
{mobile ? (
|
|
32
|
+
<Mobile />
|
|
33
|
+
) : (
|
|
34
|
+
<Layout>
|
|
35
|
+
<Header settings={false} />
|
|
36
|
+
<Flexbox flex={1} height={'calc(100vh - 64px)'} horizontal>
|
|
37
|
+
<Conversation />
|
|
38
|
+
<SideBar systemRole={false} />
|
|
39
|
+
</Flexbox>
|
|
40
|
+
</Layout>
|
|
41
|
+
)}
|
|
36
42
|
</>
|
|
37
43
|
);
|
|
38
44
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useResponsive } from 'antd-style';
|
|
1
2
|
import { PropsWithChildren, memo } from 'react';
|
|
2
3
|
import { Flexbox } from 'react-layout-kit';
|
|
3
4
|
|
|
@@ -7,19 +8,22 @@ import { useSwitchSideBarOnInit } from '@/store/global';
|
|
|
7
8
|
import { Sessions } from './SessionList';
|
|
8
9
|
|
|
9
10
|
const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
|
11
|
+
const { mobile } = useResponsive();
|
|
10
12
|
useSwitchSideBarOnInit('chat');
|
|
11
13
|
|
|
12
14
|
return (
|
|
13
15
|
<AppLayout>
|
|
14
16
|
<Sessions />
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
{!mobile && (
|
|
18
|
+
<Flexbox
|
|
19
|
+
flex={1}
|
|
20
|
+
height={'100vh'}
|
|
21
|
+
id={'lobe-conversion-container'}
|
|
22
|
+
style={{ position: 'relative' }}
|
|
23
|
+
>
|
|
24
|
+
{children}
|
|
25
|
+
</Flexbox>
|
|
26
|
+
)}
|
|
23
27
|
</AppLayout>
|
|
24
28
|
);
|
|
25
29
|
});
|