@lobehub/chat 0.81.0 → 0.82.2
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 +75 -0
- package/README.md +22 -6
- package/README.zh-CN.md +21 -5
- package/docs/Environment-Variable.md +45 -9
- package/docs/Environment-Variable.zh-CN.md +3 -6
- package/locales/en_US/common.json +13 -1
- package/locales/ru_RU/common.json +13 -1
- package/locales/zh_CN/common.json +13 -1
- package/locales/zh_TW/common.json +13 -1
- package/package.json +6 -3
- package/public/images/screenshot_background.webp +0 -0
- package/public/manifest.json +2 -16
- package/src/app/chat/features/Conversation/ChatList/Error/InvalidAccess.tsx +16 -6
- package/src/app/chat/features/Header/Mobile.tsx +12 -7
- package/src/app/chat/features/Header/ShareButton/Inner.tsx +114 -14
- package/src/app/chat/features/Header/ShareButton/Preview.tsx +140 -0
- package/src/app/chat/features/Header/ShareButton/index.tsx +12 -2
- package/src/app/chat/features/Header/ShareButton/style.ts +71 -0
- package/src/app/chat/features/SessionList/Header/Desktop.tsx +2 -4
- package/src/app/chat/features/SessionList/Header/Mobile.tsx +15 -14
- package/src/app/chat/features/Sidebar/Mobile.tsx +3 -17
- package/src/app/chat/layout.desktop.tsx +2 -1
- package/src/app/chat/layout.mobile.tsx +2 -1
- package/src/app/chat/settings/features/Header/index.tsx +3 -3
- package/src/app/chat/settings/layout.mobile.tsx +1 -8
- package/src/app/market/features/AgentCard/AgentCardBanner.tsx +2 -1
- package/src/app/market/features/Header/Mobile.tsx +2 -21
- package/src/app/market/features/SideBar/AgentInfo/Header.tsx +2 -1
- package/src/app/market/features/SideBar/AgentInfo/index.tsx +10 -2
- package/src/app/market/features/SideBar/Mobile.tsx +3 -17
- package/src/app/market/layout.desktop.tsx +2 -1
- package/src/app/market/layout.mobile.tsx +2 -1
- package/src/app/settings/agent/index.tsx +20 -0
- package/src/app/settings/agent/page.tsx +7 -0
- package/src/app/settings/{features/Settings → common}/Common.tsx +1 -1
- package/src/app/settings/common/index.tsx +20 -0
- package/src/app/settings/common/page.tsx +7 -0
- package/src/app/settings/features/Header/Desktop.tsx +9 -2
- package/src/app/settings/features/Header/Mobile.tsx +5 -2
- package/src/app/settings/features/SideBar/Item.tsx +39 -0
- package/src/app/settings/features/SideBar/List.tsx +30 -0
- package/src/app/settings/features/SideBar/index.tsx +38 -0
- package/src/app/settings/index.tsx +5 -7
- package/src/app/settings/layout.desktop.tsx +6 -2
- package/src/app/settings/layout.mobile.tsx +1 -3
- package/src/app/settings/{features/Settings → llm}/LLM/index.tsx +4 -2
- package/src/app/settings/llm/index.tsx +20 -0
- package/src/app/settings/llm/page.tsx +7 -0
- package/src/app/settings/mobile/ExtraList.tsx +61 -0
- package/src/app/settings/mobile/index.tsx +43 -0
- package/src/app/settings/mobile/page.tsx +7 -0
- package/src/app/settings/page.tsx +1 -7
- package/src/components/MobilePadding/index.tsx +32 -0
- package/src/const/layoutTokens.ts +1 -0
- package/src/features/AgentSetting/AgentMeta/index.tsx +0 -1
- package/src/features/AgentSetting/AgentPlugin/MarketSettingModal.tsx +29 -16
- package/src/features/AvatarWithUpload/index.tsx +23 -45
- package/src/features/MobileTabBar/index.tsx +15 -3
- package/src/features/PluginDevModal/PluginPreview.tsx +11 -4
- package/src/features/PluginDevModal/index.tsx +14 -10
- package/src/features/SideBar/BottomActions.tsx +3 -3
- package/src/features/SideBar/TopActions.tsx +5 -4
- package/src/features/SideBar/index.tsx +1 -1
- package/src/locales/create.ts +5 -3
- package/src/locales/default/common.ts +13 -1
- package/src/locales/default/plugin.ts +2 -1
- package/src/services/shareGPT.ts +11 -1
- package/src/store/global/hooks/useSwitchSettingsOnInit.ts +9 -0
- package/src/store/global/initialState.ts +13 -4
- package/src/store/global/store.ts +0 -4
- package/src/store/session/slices/chat/actions/share.ts +73 -39
- package/src/styles/antdOverride.ts +2 -26
- package/src/styles/global.ts +9 -0
- package/src/utils/imageToBase64.ts +37 -0
- package/src/utils/parseMarkdown.ts +9 -0
- package/public/icons/icon-maskable-192x192.png +0 -0
- package/public/icons/icon-maskable-512x512.png +0 -0
- package/src/app/settings/features/Banner/index.tsx +0 -94
- package/src/app/settings/features/Settings/index.tsx +0 -53
- /package/src/app/settings/{features/Settings → agent}/Agent.tsx +0 -0
- /package/src/app/settings/{features/Settings → llm}/LLM/Checker.tsx +0 -0
- /package/src/app/settings/{features/Settings → llm}/LLM/ModelList.tsx +0 -0
- /package/src/app/settings/{features/Settings → llm}/LLM/getModelList.ts +0 -0
|
@@ -4,6 +4,8 @@ import { useRouter } from 'next/navigation';
|
|
|
4
4
|
import { memo } from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
|
|
7
|
+
import ShareButton from '@/app/chat/features/Header/ShareButton';
|
|
8
|
+
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
7
9
|
import { useGlobalStore } from '@/store/global';
|
|
8
10
|
import { useSessionStore } from '@/store/session';
|
|
9
11
|
import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
|
|
@@ -13,10 +15,9 @@ const MobileHeader = memo(() => {
|
|
|
13
15
|
const { t } = useTranslation('common');
|
|
14
16
|
const router = useRouter();
|
|
15
17
|
|
|
16
|
-
const [isInbox, title
|
|
18
|
+
const [isInbox, title] = useSessionStore((s) => [
|
|
17
19
|
sessionSelectors.isInboxSession(s),
|
|
18
20
|
agentSelectors.currentAgentTitle(s),
|
|
19
|
-
agentSelectors.currentAgentModel(s),
|
|
20
21
|
]);
|
|
21
22
|
|
|
22
23
|
const [toggleConfig] = useGlobalStore((s) => [s.toggleMobileTopic]);
|
|
@@ -25,17 +26,21 @@ const MobileHeader = memo(() => {
|
|
|
25
26
|
|
|
26
27
|
return (
|
|
27
28
|
<MobileNavBar
|
|
28
|
-
center={<MobileNavBarTitle
|
|
29
|
+
center={<MobileNavBarTitle title={displayTitle} />}
|
|
29
30
|
onBackClick={() => router.push('/chat')}
|
|
30
31
|
right={
|
|
31
32
|
<>
|
|
32
|
-
<
|
|
33
|
+
<ShareButton />
|
|
34
|
+
<ActionIcon
|
|
35
|
+
icon={LayoutList}
|
|
36
|
+
onClick={() => toggleConfig()}
|
|
37
|
+
size={MOBILE_HEADER_ICON_SIZE}
|
|
38
|
+
/>
|
|
33
39
|
{!isInbox && (
|
|
34
40
|
<ActionIcon
|
|
35
41
|
icon={Settings}
|
|
36
|
-
onClick={() => {
|
|
37
|
-
|
|
38
|
-
}}
|
|
42
|
+
onClick={() => router.push(pathString('/chat/settings', { hash: location.hash }))}
|
|
43
|
+
size={MOBILE_HEADER_ICON_SIZE}
|
|
39
44
|
/>
|
|
40
45
|
)}
|
|
41
46
|
</>
|
|
@@ -1,28 +1,128 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { Form, ItemGroup } from '@lobehub/ui';
|
|
2
|
+
import { Button, Segmented, SegmentedProps, Switch } from 'antd';
|
|
3
|
+
import { Share2 } from 'lucide-react';
|
|
4
|
+
import { memo, useMemo, useState } from 'react';
|
|
4
5
|
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
7
|
|
|
8
|
+
import MobilePadding from '@/components/MobilePadding';
|
|
9
|
+
import { FORM_STYLE } from '@/const/layoutTokens';
|
|
10
|
+
import { useGlobalStore } from '@/store/global';
|
|
6
11
|
import { useSessionStore } from '@/store/session';
|
|
7
12
|
|
|
8
|
-
|
|
13
|
+
import Preview, { ImageType, imageTypeOptions } from './Preview';
|
|
14
|
+
|
|
15
|
+
enum Tab {
|
|
16
|
+
Screenshot = 'screenshot',
|
|
17
|
+
ShareGPT = 'sharegpt',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const Inner = memo(() => {
|
|
21
|
+
const [withSystemRole, setWithSystemRole] = useState(false);
|
|
22
|
+
const [withPluginInfo, setWithPluginInfo] = useState(false);
|
|
23
|
+
const [withBackground, setWithBackground] = useState(true);
|
|
24
|
+
const [withFooter, setWithFooter] = useState(true);
|
|
25
|
+
const [imageType, setImageType] = useState<ImageType>(ImageType.JPG);
|
|
26
|
+
const [tab, setTab] = useState<Tab>(Tab.Screenshot);
|
|
9
27
|
const { t } = useTranslation('common');
|
|
10
28
|
|
|
29
|
+
const avatar = useGlobalStore((s) => s.settings.avatar);
|
|
11
30
|
const [shareLoading, shareToShareGPT] = useSessionStore((s) => [
|
|
12
31
|
s.shareLoading,
|
|
13
32
|
s.shareToShareGPT,
|
|
14
33
|
]);
|
|
15
34
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
const options: SegmentedProps['options'] = useMemo(
|
|
36
|
+
() => [
|
|
37
|
+
{
|
|
38
|
+
label: t('shareModal.screenshot'),
|
|
39
|
+
value: Tab.Screenshot,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
label: 'ShareGPT',
|
|
43
|
+
value: Tab.ShareGPT,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
[],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const settings: ItemGroup = useMemo(
|
|
50
|
+
() =>
|
|
51
|
+
({
|
|
52
|
+
children: [
|
|
53
|
+
{
|
|
54
|
+
children: <Switch checked={withSystemRole} onChange={setWithSystemRole} />,
|
|
55
|
+
label: t('shareModal.withSystemRole'),
|
|
56
|
+
minWidth: undefined,
|
|
57
|
+
},
|
|
58
|
+
tab === Tab.Screenshot && {
|
|
59
|
+
children: <Switch checked={withBackground} onChange={setWithBackground} />,
|
|
60
|
+
label: t('shareModal.withBackground'),
|
|
61
|
+
minWidth: undefined,
|
|
62
|
+
},
|
|
63
|
+
tab === Tab.Screenshot && {
|
|
64
|
+
children: <Switch checked={withFooter} onChange={setWithFooter} />,
|
|
65
|
+
label: t('shareModal.withFooter'),
|
|
66
|
+
minWidth: undefined,
|
|
67
|
+
},
|
|
68
|
+
tab === Tab.Screenshot && {
|
|
69
|
+
children: (
|
|
70
|
+
<Segmented
|
|
71
|
+
onChange={(value) => setImageType(value as ImageType)}
|
|
72
|
+
options={imageTypeOptions}
|
|
73
|
+
value={imageType}
|
|
74
|
+
/>
|
|
75
|
+
),
|
|
76
|
+
label: t('shareModal.imageType'),
|
|
77
|
+
minWidth: undefined,
|
|
78
|
+
},
|
|
79
|
+
tab === Tab.ShareGPT && {
|
|
80
|
+
children: <Switch checked={withPluginInfo} onChange={setWithPluginInfo} />,
|
|
81
|
+
label: t('shareModal.withPluginInfo'),
|
|
82
|
+
minWidth: undefined,
|
|
83
|
+
},
|
|
84
|
+
].filter(Boolean),
|
|
85
|
+
icon: Share2,
|
|
86
|
+
title: t('shareModal.settings'),
|
|
87
|
+
}) as ItemGroup,
|
|
88
|
+
[tab, withSystemRole, withPluginInfo, withBackground, withFooter, imageType],
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Flexbox gap={16}>
|
|
93
|
+
<MobilePadding bottom={0}>
|
|
94
|
+
<Segmented
|
|
95
|
+
block
|
|
96
|
+
onChange={(value) => setTab(value as Tab)}
|
|
97
|
+
options={options}
|
|
98
|
+
style={{ width: '100%' }}
|
|
99
|
+
value={tab}
|
|
100
|
+
/>
|
|
101
|
+
</MobilePadding>
|
|
102
|
+
<Form items={[settings]} {...FORM_STYLE} />
|
|
103
|
+
<MobilePadding gap={16} top={0}>
|
|
104
|
+
{tab === Tab.Screenshot && (
|
|
105
|
+
<Preview
|
|
106
|
+
imageType={imageType}
|
|
107
|
+
withBackground={withBackground}
|
|
108
|
+
withFooter={withFooter}
|
|
109
|
+
withSystemRole={withSystemRole}
|
|
110
|
+
/>
|
|
111
|
+
)}
|
|
112
|
+
{tab === Tab.ShareGPT && (
|
|
113
|
+
<Button
|
|
114
|
+
block
|
|
115
|
+
loading={shareLoading}
|
|
116
|
+
onClick={() => shareToShareGPT({ avatar, withPluginInfo, withSystemRole })}
|
|
117
|
+
size={'large'}
|
|
118
|
+
type={'primary'}
|
|
119
|
+
>
|
|
120
|
+
{t('shareModal.shareToShareGPT')}
|
|
121
|
+
</Button>
|
|
122
|
+
)}
|
|
123
|
+
</MobilePadding>
|
|
124
|
+
</Flexbox>
|
|
25
125
|
);
|
|
26
126
|
});
|
|
27
127
|
|
|
28
|
-
export default
|
|
128
|
+
export default Inner;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { SiOpenai } from '@icons-pack/react-simple-icons';
|
|
2
|
+
import { Avatar, ChatHeaderTitle, Logo, Markdown, Tag } from '@lobehub/ui';
|
|
3
|
+
import { Button, SegmentedProps } from 'antd';
|
|
4
|
+
import dayjs from 'dayjs';
|
|
5
|
+
import { toJpeg, toPng, toSvg } from 'html-to-image';
|
|
6
|
+
import { memo, useCallback, useState } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
import pkg from '@/../package.json';
|
|
11
|
+
import ChatList from '@/app/chat/features/Conversation/ChatList';
|
|
12
|
+
import PluginTag from '@/app/chat/features/Header/PluginTag';
|
|
13
|
+
import { useSessionStore } from '@/store/session';
|
|
14
|
+
import { agentSelectors } from '@/store/session/slices/agentConfig';
|
|
15
|
+
import { sessionSelectors } from '@/store/session/slices/session/selectors';
|
|
16
|
+
|
|
17
|
+
import { useStyles } from './style';
|
|
18
|
+
|
|
19
|
+
export enum ImageType {
|
|
20
|
+
JPG = 'jpg',
|
|
21
|
+
PNG = 'png',
|
|
22
|
+
SVG = 'svg',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const imageTypeOptions: SegmentedProps['options'] = [
|
|
26
|
+
{
|
|
27
|
+
label: 'JPG',
|
|
28
|
+
value: ImageType.JPG,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: 'PNG',
|
|
32
|
+
value: ImageType.PNG,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: 'SVG',
|
|
36
|
+
value: ImageType.SVG,
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
interface PreviewProps {
|
|
41
|
+
imageType: ImageType;
|
|
42
|
+
withBackground: boolean;
|
|
43
|
+
withFooter: boolean;
|
|
44
|
+
withSystemRole: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const Preview = memo<PreviewProps>(({ withSystemRole, imageType, withBackground, withFooter }) => {
|
|
48
|
+
const [loading, setLoading] = useState(false);
|
|
49
|
+
const [isInbox, title, description, avatar, backgroundColor, model, plugins, systemRole] =
|
|
50
|
+
useSessionStore((s) => [
|
|
51
|
+
sessionSelectors.isInboxSession(s),
|
|
52
|
+
agentSelectors.currentAgentTitle(s),
|
|
53
|
+
agentSelectors.currentAgentDescription(s),
|
|
54
|
+
agentSelectors.currentAgentAvatar(s),
|
|
55
|
+
agentSelectors.currentAgentBackgroundColor(s),
|
|
56
|
+
agentSelectors.currentAgentModel(s),
|
|
57
|
+
agentSelectors.currentAgentPlugins(s),
|
|
58
|
+
agentSelectors.currentAgentSystemRole(s),
|
|
59
|
+
]);
|
|
60
|
+
const { t } = useTranslation('common');
|
|
61
|
+
const { styles } = useStyles(withBackground);
|
|
62
|
+
|
|
63
|
+
const displayTitle = isInbox ? t('inbox.title') : title;
|
|
64
|
+
const displayDesc = isInbox ? t('inbox.desc') : description;
|
|
65
|
+
|
|
66
|
+
const handleDownload = useCallback(async () => {
|
|
67
|
+
setLoading(true);
|
|
68
|
+
try {
|
|
69
|
+
let screenshotFn: any;
|
|
70
|
+
switch (imageType) {
|
|
71
|
+
case ImageType.JPG: {
|
|
72
|
+
screenshotFn = toJpeg;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
case ImageType.PNG: {
|
|
76
|
+
screenshotFn = toPng;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case ImageType.SVG: {
|
|
80
|
+
screenshotFn = toSvg;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const dataUrl = await screenshotFn(document.querySelector('#preview') as HTMLDivElement);
|
|
85
|
+
const link = document.createElement('a');
|
|
86
|
+
link.download = `LobeChat_${title}_${dayjs().format('YYYY-MM-DD')}.${imageType}`;
|
|
87
|
+
link.href = dataUrl;
|
|
88
|
+
link.click();
|
|
89
|
+
setLoading(false);
|
|
90
|
+
} catch {
|
|
91
|
+
console.error('Failed to download image');
|
|
92
|
+
setLoading(false);
|
|
93
|
+
}
|
|
94
|
+
}, [imageType, title]);
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<>
|
|
98
|
+
<div className={styles.preview}>
|
|
99
|
+
<div className={withBackground ? styles.background : undefined} id={'preview'}>
|
|
100
|
+
<Flexbox className={styles.container} gap={16}>
|
|
101
|
+
<div className={styles.header}>
|
|
102
|
+
<Flexbox align={'flex-start'} gap={12} horizontal>
|
|
103
|
+
<Avatar avatar={avatar} background={backgroundColor} size={40} title={title} />
|
|
104
|
+
<ChatHeaderTitle
|
|
105
|
+
desc={displayDesc}
|
|
106
|
+
tag={
|
|
107
|
+
<>
|
|
108
|
+
<Tag icon={<SiOpenai size={'1em'} />}>{model}</Tag>
|
|
109
|
+
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
|
|
110
|
+
</>
|
|
111
|
+
}
|
|
112
|
+
title={displayTitle}
|
|
113
|
+
/>
|
|
114
|
+
</Flexbox>
|
|
115
|
+
{withSystemRole && systemRole && (
|
|
116
|
+
<div className={styles.role}>
|
|
117
|
+
<Markdown>{systemRole}</Markdown>
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
</div>
|
|
121
|
+
<ChatList />
|
|
122
|
+
{withFooter ? (
|
|
123
|
+
<Flexbox align={'center'} className={styles.footer} gap={4}>
|
|
124
|
+
<Logo extra={'chat'} type={'combine'} />
|
|
125
|
+
<div className={styles.url}>{pkg.homepage}</div>
|
|
126
|
+
</Flexbox>
|
|
127
|
+
) : (
|
|
128
|
+
<div />
|
|
129
|
+
)}
|
|
130
|
+
</Flexbox>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
<Button block loading={loading} onClick={handleDownload} size={'large'} type={'primary'}>
|
|
134
|
+
{t('shareModal.download')}
|
|
135
|
+
</Button>
|
|
136
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
export default Preview;
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { ActionIcon, Modal } from '@lobehub/ui';
|
|
2
|
+
import { useResponsive } from 'antd-style';
|
|
2
3
|
import { Share2 } from 'lucide-react';
|
|
3
4
|
import { memo, useState } from 'react';
|
|
4
5
|
import { useTranslation } from 'react-i18next';
|
|
5
6
|
|
|
7
|
+
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
6
8
|
import { useSessionStore } from '@/store/session';
|
|
7
9
|
|
|
10
|
+
import Inner from './Inner';
|
|
11
|
+
|
|
8
12
|
const ShareButton = memo(() => {
|
|
9
13
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
10
14
|
const { t } = useTranslation('common');
|
|
11
15
|
const [shareLoading] = useSessionStore((s) => [s.shareLoading]);
|
|
16
|
+
const { mobile } = useResponsive();
|
|
17
|
+
|
|
18
|
+
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
|
|
12
19
|
|
|
13
20
|
return (
|
|
14
21
|
<>
|
|
@@ -16,15 +23,18 @@ const ShareButton = memo(() => {
|
|
|
16
23
|
icon={Share2}
|
|
17
24
|
loading={shareLoading}
|
|
18
25
|
onClick={() => setIsModalOpen(true)}
|
|
19
|
-
size={
|
|
26
|
+
size={size}
|
|
20
27
|
title={t('share')}
|
|
21
28
|
/>
|
|
22
29
|
<Modal
|
|
30
|
+
centered={false}
|
|
23
31
|
footer={null}
|
|
24
32
|
onCancel={() => setIsModalOpen(false)}
|
|
25
33
|
open={isModalOpen}
|
|
26
34
|
title={t('share')}
|
|
27
|
-
|
|
35
|
+
>
|
|
36
|
+
<Inner />
|
|
37
|
+
</Modal>
|
|
28
38
|
</>
|
|
29
39
|
);
|
|
30
40
|
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createStyles } from 'antd-style';
|
|
2
|
+
import { rgba } from 'polished';
|
|
3
|
+
|
|
4
|
+
export const useStyles = createStyles(({ css, token, stylish, cx }, withBackground: boolean) => ({
|
|
5
|
+
background: css`
|
|
6
|
+
padding: 24px;
|
|
7
|
+
background: url('/images/screenshot_background.webp') no-repeat;
|
|
8
|
+
background-size: 100% 100%;
|
|
9
|
+
`,
|
|
10
|
+
container: cx(
|
|
11
|
+
withBackground &&
|
|
12
|
+
css`
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
border: 1px solid ${rgba(token.colorText, 0.15)};
|
|
15
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
16
|
+
box-shadow:
|
|
17
|
+
0 8px 16px -4px rgba(0, 0, 0, 75%),
|
|
18
|
+
0 8px 24px -4px rgba(0, 0, 0, 75%);
|
|
19
|
+
`,
|
|
20
|
+
css`
|
|
21
|
+
background: ${token.colorBgLayout};
|
|
22
|
+
`,
|
|
23
|
+
),
|
|
24
|
+
footer: css`
|
|
25
|
+
padding: 16px;
|
|
26
|
+
border-top: 1px solid ${token.colorBorder};
|
|
27
|
+
`,
|
|
28
|
+
header: css`
|
|
29
|
+
margin-bottom: -24px;
|
|
30
|
+
padding: 16px;
|
|
31
|
+
background: ${token.colorBgContainer};
|
|
32
|
+
border-bottom: 1px solid ${token.colorBorder};
|
|
33
|
+
`,
|
|
34
|
+
preview: cx(
|
|
35
|
+
stylish.noScrollbar,
|
|
36
|
+
css`
|
|
37
|
+
overflow-x: hidden;
|
|
38
|
+
overflow-y: scroll;
|
|
39
|
+
|
|
40
|
+
width: 100%;
|
|
41
|
+
max-height: 40vh;
|
|
42
|
+
|
|
43
|
+
background: ${token.colorBgLayout};
|
|
44
|
+
border: 1px solid ${token.colorBorder};
|
|
45
|
+
border-radius: ${token.borderRadiusLG}px;
|
|
46
|
+
|
|
47
|
+
* {
|
|
48
|
+
pointer-events: none;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
|
|
51
|
+
::-webkit-scrollbar {
|
|
52
|
+
width: 0 !important;
|
|
53
|
+
height: 0 !important;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`,
|
|
57
|
+
),
|
|
58
|
+
role: css`
|
|
59
|
+
margin-top: 12px;
|
|
60
|
+
padding-top: 12px;
|
|
61
|
+
opacity: 0.75;
|
|
62
|
+
border-top: 1px dashed ${token.colorBorderSecondary};
|
|
63
|
+
|
|
64
|
+
* {
|
|
65
|
+
font-size: 12px !important;
|
|
66
|
+
}
|
|
67
|
+
`,
|
|
68
|
+
url: css`
|
|
69
|
+
color: ${token.colorTextDescription};
|
|
70
|
+
`,
|
|
71
|
+
}));
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ActionIcon, Logo } from '@lobehub/ui';
|
|
2
2
|
import { createStyles } from 'antd-style';
|
|
3
3
|
import { MessageSquarePlus } from 'lucide-react';
|
|
4
|
-
import Link from 'next/link';
|
|
5
4
|
import { memo } from 'react';
|
|
6
5
|
import { useTranslation } from 'react-i18next';
|
|
7
6
|
import { Flexbox } from 'react-layout-kit';
|
|
@@ -28,9 +27,8 @@ const Header = memo(() => {
|
|
|
28
27
|
return (
|
|
29
28
|
<Flexbox className={styles.top} gap={16} padding={16}>
|
|
30
29
|
<Flexbox distribution={'space-between'} horizontal>
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
</Link>
|
|
30
|
+
<Logo className={styles.logo} size={36} type={'text'} />
|
|
31
|
+
|
|
34
32
|
<ActionIcon
|
|
35
33
|
icon={MessageSquarePlus}
|
|
36
34
|
onClick={() => createSession()}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { ActionIcon, Logo, MobileNavBar } from '@lobehub/ui';
|
|
1
|
+
import { ActionIcon, Avatar, Logo, MobileNavBar } from '@lobehub/ui';
|
|
2
2
|
import { createStyles } from 'antd-style';
|
|
3
|
-
import { MessageSquarePlus
|
|
3
|
+
import { MessageSquarePlus } from 'lucide-react';
|
|
4
4
|
import { useRouter } from 'next/navigation';
|
|
5
5
|
import { memo } from 'react';
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
8
|
+
import { useGlobalStore } from '@/store/global';
|
|
8
9
|
import { useSessionStore } from '@/store/session';
|
|
9
10
|
|
|
10
11
|
export const useStyles = createStyles(({ css, token }) => ({
|
|
@@ -20,21 +21,21 @@ export const useStyles = createStyles(({ css, token }) => ({
|
|
|
20
21
|
const Header = memo(() => {
|
|
21
22
|
const [createSession] = useSessionStore((s) => [s.createSession]);
|
|
22
23
|
const router = useRouter();
|
|
23
|
-
|
|
24
|
+
const avatar = useGlobalStore((st) => st.settings.avatar);
|
|
24
25
|
return (
|
|
25
26
|
<MobileNavBar
|
|
26
27
|
center={<Logo type={'text'} />}
|
|
27
|
-
left={
|
|
28
|
+
left={
|
|
29
|
+
<div onClick={() => router.push('/settings/mobile')} style={{ marginLeft: 8 }}>
|
|
30
|
+
{avatar ? <Avatar avatar={avatar} size={28} /> : <Logo size={28} />}
|
|
31
|
+
</div>
|
|
32
|
+
}
|
|
28
33
|
right={
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
router.push('/settings');
|
|
35
|
-
}}
|
|
36
|
-
/>
|
|
37
|
-
</>
|
|
34
|
+
<ActionIcon
|
|
35
|
+
icon={MessageSquarePlus}
|
|
36
|
+
onClick={() => createSession()}
|
|
37
|
+
size={MOBILE_HEADER_ICON_SIZE}
|
|
38
|
+
/>
|
|
38
39
|
}
|
|
39
40
|
/>
|
|
40
41
|
);
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Drawer } from 'antd';
|
|
3
|
-
import { useTheme } from 'antd-style';
|
|
4
|
-
import { X } from 'lucide-react';
|
|
1
|
+
import { Modal } from '@lobehub/ui';
|
|
5
2
|
import { PropsWithChildren, memo } from 'react';
|
|
6
3
|
import { useTranslation } from 'react-i18next';
|
|
7
4
|
|
|
8
5
|
import { useGlobalStore } from '@/store/global';
|
|
9
6
|
|
|
10
7
|
const Mobile = memo<PropsWithChildren>(({ children }) => {
|
|
11
|
-
const theme = useTheme();
|
|
12
8
|
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
|
|
13
9
|
s.preference.mobileShowTopic,
|
|
14
10
|
s.toggleMobileTopic,
|
|
@@ -17,19 +13,9 @@ const Mobile = memo<PropsWithChildren>(({ children }) => {
|
|
|
17
13
|
const { t } = useTranslation('common');
|
|
18
14
|
|
|
19
15
|
return (
|
|
20
|
-
<
|
|
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
|
-
>
|
|
16
|
+
<Modal onCancel={() => toggleConfig(false)} open={showAgentSettings} title={t('topic.title')}>
|
|
31
17
|
{children}
|
|
32
|
-
</
|
|
18
|
+
</Modal>
|
|
33
19
|
);
|
|
34
20
|
});
|
|
35
21
|
|
|
@@ -3,11 +3,12 @@ import { Flexbox } from 'react-layout-kit';
|
|
|
3
3
|
|
|
4
4
|
import AppLayout from '@/layout/AppLayout';
|
|
5
5
|
import { useSwitchSideBarOnInit } from '@/store/global';
|
|
6
|
+
import { SidebarTabKey } from '@/store/global/initialState';
|
|
6
7
|
|
|
7
8
|
import { Sessions } from './features/SessionList';
|
|
8
9
|
|
|
9
10
|
const ChatLayout = memo<PropsWithChildren>(({ children }) => {
|
|
10
|
-
useSwitchSideBarOnInit(
|
|
11
|
+
useSwitchSideBarOnInit(SidebarTabKey.Chat);
|
|
11
12
|
|
|
12
13
|
return (
|
|
13
14
|
<AppLayout>
|
|
@@ -2,12 +2,13 @@ import { memo } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import AppMobileLayout from '@/layout/AppMobileLayout';
|
|
4
4
|
import { useSwitchSideBarOnInit } from '@/store/global';
|
|
5
|
+
import { SidebarTabKey } from '@/store/global/initialState';
|
|
5
6
|
|
|
6
7
|
import { Sessions } from './features/SessionList';
|
|
7
8
|
import Header from './features/SessionList/Header';
|
|
8
9
|
|
|
9
10
|
const ChatMobileLayout = memo(() => {
|
|
10
|
-
useSwitchSideBarOnInit(
|
|
11
|
+
useSwitchSideBarOnInit(SidebarTabKey.Chat);
|
|
11
12
|
|
|
12
13
|
return (
|
|
13
14
|
<AppMobileLayout navBar={<Header />} showTabBar>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ActionIcon } from '@lobehub/ui';
|
|
2
2
|
import { Dropdown, MenuProps } from 'antd';
|
|
3
3
|
import { useResponsive } from 'antd-style';
|
|
4
|
-
import { HardDriveDownload
|
|
4
|
+
import { HardDriveDownload } from 'lucide-react';
|
|
5
5
|
import { memo, useMemo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
|
+
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
8
9
|
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
|
9
10
|
import { useSessionStore } from '@/store/session';
|
|
10
11
|
|
|
@@ -42,11 +43,10 @@ const Header = memo<{ mobile?: boolean }>(() => {
|
|
|
42
43
|
[],
|
|
43
44
|
);
|
|
44
45
|
|
|
45
|
-
const size = mobile ?
|
|
46
|
+
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
|
|
46
47
|
|
|
47
48
|
return (
|
|
48
49
|
<Render>
|
|
49
|
-
<ActionIcon icon={Share2} size={size} title={t('share', { ns: 'common' })} />
|
|
50
50
|
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
|
51
51
|
<ActionIcon icon={HardDriveDownload} size={size} title={t('export', { ns: 'common' })} />
|
|
52
52
|
</Dropdown>
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { PropsWithChildren, memo } from 'react';
|
|
2
|
-
import { Flexbox } from 'react-layout-kit';
|
|
3
2
|
|
|
4
3
|
import AppMobileLayout from '@/layout/AppMobileLayout';
|
|
5
4
|
|
|
6
5
|
import Header from './features/Header';
|
|
7
6
|
|
|
8
7
|
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
|
|
9
|
-
return
|
|
10
|
-
<AppMobileLayout navBar={<Header />}>
|
|
11
|
-
<Flexbox gap={16} padding={16}>
|
|
12
|
-
{children}
|
|
13
|
-
</Flexbox>
|
|
14
|
-
</AppMobileLayout>
|
|
15
|
-
);
|
|
8
|
+
return <AppMobileLayout navBar={<Header />}>{children}</AppMobileLayout>;
|
|
16
9
|
});
|
|
17
10
|
|
|
18
11
|
export default MobileLayout;
|
|
@@ -39,7 +39,7 @@ interface AgentCardBannerProps extends DivProps {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const AgentCardBanner = memo<AgentCardBannerProps>(
|
|
42
|
-
({ meta, className, mask, size = 8, maskColor, ...props }) => {
|
|
42
|
+
({ meta, className, mask, size = 8, maskColor, children, ...props }) => {
|
|
43
43
|
const { styles, theme, cx } = useStyles(maskColor);
|
|
44
44
|
|
|
45
45
|
return (
|
|
@@ -56,6 +56,7 @@ const AgentCardBanner = memo<AgentCardBannerProps>(
|
|
|
56
56
|
style={{ transform: `scale(${size})` }}
|
|
57
57
|
/>
|
|
58
58
|
{mask && <div className={styles.bannerMask} />}
|
|
59
|
+
{children}
|
|
59
60
|
</Flexbox>
|
|
60
61
|
);
|
|
61
62
|
},
|
|
@@ -1,27 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Settings2 } from 'lucide-react';
|
|
3
|
-
import { useRouter } from 'next/navigation';
|
|
1
|
+
import { Logo, MobileNavBar } from '@lobehub/ui';
|
|
4
2
|
import { memo } from 'react';
|
|
5
3
|
|
|
6
|
-
import AvatarWithUpload from '@/features/AvatarWithUpload';
|
|
7
|
-
|
|
8
4
|
const Header = memo(() => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
return (
|
|
12
|
-
<MobileNavBar
|
|
13
|
-
center={<Logo type={'text'} />}
|
|
14
|
-
left={<AvatarWithUpload size={28} style={{ marginLeft: 8 }} />}
|
|
15
|
-
right={
|
|
16
|
-
<ActionIcon
|
|
17
|
-
icon={Settings2}
|
|
18
|
-
onClick={() => {
|
|
19
|
-
router.push('/settings');
|
|
20
|
-
}}
|
|
21
|
-
/>
|
|
22
|
-
}
|
|
23
|
-
/>
|
|
24
|
-
);
|
|
5
|
+
return <MobileNavBar center={<Logo type={'text'} />} />;
|
|
25
6
|
});
|
|
26
7
|
|
|
27
8
|
export default Header;
|