♻️ refactor: Refactor settings page and mobile ux

This commit is contained in:
canisminor1990 2023-09-29 03:41:32 +08:00
parent 05f568a5fe
commit 89c5648544
65 changed files with 582 additions and 381 deletions

View file

@ -36,7 +36,7 @@
"historyRange": "History Range",
"import": "Import Configuration",
"inbox": {
"defaultMessage": "Hello, I'm your intelligent agent. You can ask me any questions and I will do my best to answer you. If you need a more professional or customized agent, you can click <kbd>+</kbd> to create a custom agent.",
"defaultMessage": "Hello, I'm your intelligent agent. You can ask me any questions and I will do my best to answer you. If you need a more professional or customized agent, you can click `+` to create a custom agent.",
"desc": "Activate the brain cluster and spark your thinking. Your intelligent agent is here to communicate with you about everything.",
"title": "Chat Freely"
},

View file

@ -36,7 +36,7 @@
"historyRange": "Исторический диапазон",
"import": "Импорт конфига",
"inbox": {
"defaultMessage": "Hello, I'm your intelligent assistant. You can ask me any questions and I will do my best to answer you. If you need a more professional or customized assistant, you can click <kbd>+</kbd> to create a custom assistant.",
"defaultMessage": "Hello, I'm your intelligent assistant. You can ask me any questions and I will do my best to answer you. If you need a more professional or customized assistant, you can click `+` to create a custom assistant.",
"desc": "Activate the brain cluster and spark your thinking. Your intelligent assistant is here to communicate with you about everything.",
"title": "Chat Freely"
},

View file

@ -36,7 +36,7 @@
"historyRange": "历史范围",
"import": "导入配置",
"inbox": {
"defaultMessage": "你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击<kbd>+</kbd>创建自定义助手",
"defaultMessage": "你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击`+`创建自定义助手",
"desc": "开启大脑集群,激发思维火花。你的智能助理,在这里与你交流一切",
"title": "随便聊聊"
},

View file

@ -36,7 +36,7 @@
"historyRange": "歷史範圍",
"import": "匯入設定",
"inbox": {
"defaultMessage": "您好,我是您的智慧助理。您可以問我任何問題,我會盡力回答您。如果您需要更專業或自訂的助理,您可以點選 <kbd>+</kbd> 來建立一個自訂助理。",
"defaultMessage": "您好,我是您的智慧助理。您可以問我任何問題,我會盡力回答您。如果您需要更專業或自訂的助理,您可以點選 `+` 來建立一個自訂助理。",
"desc": "啟動思維並激發你的創意。你的智慧助理已經準備好與你討論所有事情。",
"title": "隨便聊聊"
},

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 5 KiB

View file

@ -4,6 +4,8 @@ import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import ShareButton from '@/app/chat/features/Header/ShareButton';
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
import { useGlobalStore } from '@/store/global';
import { useSessionStore } from '@/store/session';
import { agentSelectors, sessionSelectors } from '@/store/session/selectors';
@ -13,10 +15,9 @@ const MobileHeader = memo(() => {
const { t } = useTranslation('common');
const router = useRouter();
const [isInbox, title, model] = useSessionStore((s) => [
const [isInbox, title] = useSessionStore((s) => [
sessionSelectors.isInboxSession(s),
agentSelectors.currentAgentTitle(s),
agentSelectors.currentAgentModel(s),
]);
const [toggleConfig] = useGlobalStore((s) => [s.toggleMobileTopic]);
@ -25,17 +26,21 @@ const MobileHeader = memo(() => {
return (
<MobileNavBar
center={<MobileNavBarTitle desc={model} title={displayTitle} />}
center={<MobileNavBarTitle title={displayTitle} />}
onBackClick={() => router.push('/chat')}
right={
<>
<ActionIcon icon={LayoutList} onClick={() => toggleConfig()} />
<ShareButton />
<ActionIcon
icon={LayoutList}
onClick={() => toggleConfig()}
size={MOBILE_HEADER_ICON_SIZE}
/>
{!isInbox && (
<ActionIcon
icon={Settings}
onClick={() => {
router.push(pathString('/chat/settings', { hash: location.hash }));
}}
onClick={() => router.push(pathString('/chat/settings', { hash: location.hash }))}
size={MOBILE_HEADER_ICON_SIZE}
/>
)}
</>

View file

@ -5,6 +5,7 @@ import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import MobilePadding from '@/components/MobilePadding';
import { FORM_STYLE } from '@/const/layoutTokens';
import { useGlobalStore } from '@/store/global';
import { useSessionStore } from '@/store/session';
@ -89,33 +90,37 @@ const Inner = memo(() => {
return (
<Flexbox gap={16}>
<Segmented
block
onChange={(value) => setTab(value as Tab)}
options={options}
style={{ width: '100%' }}
value={tab}
/>
<Form items={[settings]} {...FORM_STYLE} />
{tab === Tab.Screenshot && (
<Preview
imageType={imageType}
withBackground={withBackground}
withFooter={withFooter}
withSystemRole={withSystemRole}
/>
)}
{tab === Tab.ShareGPT && (
<Button
<MobilePadding bottom={0}>
<Segmented
block
loading={shareLoading}
onClick={() => shareToShareGPT({ avatar, withPluginInfo, withSystemRole })}
size={'large'}
type={'primary'}
>
{t('shareModal.shareToShareGPT')}
</Button>
)}
onChange={(value) => setTab(value as Tab)}
options={options}
style={{ width: '100%' }}
value={tab}
/>
</MobilePadding>
<Form items={[settings]} {...FORM_STYLE} />
<MobilePadding gap={16} top={0}>
{tab === Tab.Screenshot && (
<Preview
imageType={imageType}
withBackground={withBackground}
withFooter={withFooter}
withSystemRole={withSystemRole}
/>
)}
{tab === Tab.ShareGPT && (
<Button
block
loading={shareLoading}
onClick={() => shareToShareGPT({ avatar, withPluginInfo, withSystemRole })}
size={'large'}
type={'primary'}
>
{t('shareModal.shareToShareGPT')}
</Button>
)}
</MobilePadding>
</Flexbox>
);
});

View file

@ -1,8 +1,10 @@
import { ActionIcon, Modal } from '@lobehub/ui';
import { useResponsive } from 'antd-style';
import { Share2 } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
import { useSessionStore } from '@/store/session';
import Inner from './Inner';
@ -11,6 +13,9 @@ const ShareButton = memo(() => {
const [isModalOpen, setIsModalOpen] = useState(false);
const { t } = useTranslation('common');
const [shareLoading] = useSessionStore((s) => [s.shareLoading]);
const { mobile } = useResponsive();
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
return (
<>
@ -18,7 +23,7 @@ const ShareButton = memo(() => {
icon={Share2}
loading={shareLoading}
onClick={() => setIsModalOpen(true)}
size={{ fontSize: 24 }}
size={size}
title={t('share')}
/>
<Modal

View file

@ -1,7 +1,6 @@
import { ActionIcon, Logo } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { MessageSquarePlus } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
@ -28,9 +27,8 @@ const Header = memo(() => {
return (
<Flexbox className={styles.top} gap={16} padding={16}>
<Flexbox distribution={'space-between'} horizontal>
<Link href={'/'}>
<Logo className={styles.logo} size={36} type={'text'} />
</Link>
<Logo className={styles.logo} size={36} type={'text'} />
<ActionIcon
icon={MessageSquarePlus}
onClick={() => createSession()}

View file

@ -1,10 +1,11 @@
import { ActionIcon, Logo, MobileNavBar } from '@lobehub/ui';
import { ActionIcon, Avatar, Logo, MobileNavBar } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { MessageSquarePlus, Settings2 } from 'lucide-react';
import { MessageSquarePlus } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { memo } from 'react';
import AvatarWithUpload from '@/features/AvatarWithUpload';
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
import { useGlobalStore } from '@/store/global';
import { useSessionStore } from '@/store/session';
export const useStyles = createStyles(({ css, token }) => ({
@ -20,21 +21,21 @@ export const useStyles = createStyles(({ css, token }) => ({
const Header = memo(() => {
const [createSession] = useSessionStore((s) => [s.createSession]);
const router = useRouter();
const avatar = useGlobalStore((st) => st.settings.avatar);
return (
<MobileNavBar
center={<Logo type={'text'} />}
left={<AvatarWithUpload size={28} style={{ marginLeft: 8 }} />}
left={
<div onClick={() => router.push('/settings/mobile')} style={{ marginLeft: 8 }}>
{avatar ? <Avatar avatar={avatar} size={28} /> : <Logo size={28} />}
</div>
}
right={
<>
<ActionIcon icon={MessageSquarePlus} onClick={() => createSession()} />
<ActionIcon
icon={Settings2}
onClick={() => {
router.push('/settings');
}}
/>
</>
<ActionIcon
icon={MessageSquarePlus}
onClick={() => createSession()}
size={MOBILE_HEADER_ICON_SIZE}
/>
}
/>
);

View file

@ -1,14 +1,10 @@
import { ActionIcon } from '@lobehub/ui';
import { Drawer } from 'antd';
import { useTheme } from 'antd-style';
import { X } from 'lucide-react';
import { Modal } from '@lobehub/ui';
import { PropsWithChildren, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/store/global';
const Mobile = memo<PropsWithChildren>(({ children }) => {
const theme = useTheme();
const [showAgentSettings, toggleConfig] = useGlobalStore((s) => [
s.preference.mobileShowTopic,
s.toggleMobileTopic,
@ -17,19 +13,9 @@ const Mobile = memo<PropsWithChildren>(({ children }) => {
const { t } = useTranslation('common');
return (
<Drawer
bodyStyle={{ padding: 0 }}
closeIcon={<ActionIcon icon={X} size={{ blockSize: 32, fontSize: 20 }} />}
drawerStyle={{ background: theme.colorBgContainer }}
headerStyle={{ padding: '8px 4px' }}
height={'75vh'}
onClose={() => toggleConfig(false)}
open={showAgentSettings}
placement={'bottom'}
title={t('topic.title')}
>
<Modal onCancel={() => toggleConfig(false)} open={showAgentSettings} title={t('topic.title')}>
{children}
</Drawer>
</Modal>
);
});

View file

@ -3,11 +3,12 @@ import { Flexbox } from 'react-layout-kit';
import AppLayout from '@/layout/AppLayout';
import { useSwitchSideBarOnInit } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { Sessions } from './features/SessionList';
const ChatLayout = memo<PropsWithChildren>(({ children }) => {
useSwitchSideBarOnInit('chat');
useSwitchSideBarOnInit(SidebarTabKey.Chat);
return (
<AppLayout>

View file

@ -2,12 +2,13 @@ import { memo } from 'react';
import AppMobileLayout from '@/layout/AppMobileLayout';
import { useSwitchSideBarOnInit } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { Sessions } from './features/SessionList';
import Header from './features/SessionList/Header';
const ChatMobileLayout = memo(() => {
useSwitchSideBarOnInit('chat');
useSwitchSideBarOnInit(SidebarTabKey.Chat);
return (
<AppMobileLayout navBar={<Header />} showTabBar>

View file

@ -1,10 +1,11 @@
import { ActionIcon } from '@lobehub/ui';
import { Dropdown, MenuProps } from 'antd';
import { useResponsive } from 'antd-style';
import { HardDriveDownload, Share2 } from 'lucide-react';
import { HardDriveDownload } from 'lucide-react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
import { useSessionStore } from '@/store/session';
@ -42,11 +43,10 @@ const Header = memo<{ mobile?: boolean }>(() => {
[],
);
const size = mobile ? undefined : { fontSize: 24 };
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
return (
<Render>
<ActionIcon icon={Share2} size={size} title={t('share', { ns: 'common' })} />
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
<ActionIcon icon={HardDriveDownload} size={size} title={t('export', { ns: 'common' })} />
</Dropdown>

View file

@ -1,18 +1,11 @@
import { PropsWithChildren, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import AppMobileLayout from '@/layout/AppMobileLayout';
import Header from './features/Header';
const MobileLayout = memo<PropsWithChildren>(({ children }) => {
return (
<AppMobileLayout navBar={<Header />}>
<Flexbox gap={16} padding={16}>
{children}
</Flexbox>
</AppMobileLayout>
);
return <AppMobileLayout navBar={<Header />}>{children}</AppMobileLayout>;
});
export default MobileLayout;

View file

@ -39,7 +39,7 @@ interface AgentCardBannerProps extends DivProps {
}
const AgentCardBanner = memo<AgentCardBannerProps>(
({ meta, className, mask, size = 8, maskColor, ...props }) => {
({ meta, className, mask, size = 8, maskColor, children, ...props }) => {
const { styles, theme, cx } = useStyles(maskColor);
return (
@ -56,6 +56,7 @@ const AgentCardBanner = memo<AgentCardBannerProps>(
style={{ transform: `scale(${size})` }}
/>
{mask && <div className={styles.bannerMask} />}
{children}
</Flexbox>
);
},

View file

@ -1,27 +1,8 @@
import { ActionIcon, Logo, MobileNavBar } from '@lobehub/ui';
import { Settings2 } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { Logo, MobileNavBar } from '@lobehub/ui';
import { memo } from 'react';
import AvatarWithUpload from '@/features/AvatarWithUpload';
const Header = memo(() => {
const router = useRouter();
return (
<MobileNavBar
center={<Logo type={'text'} />}
left={<AvatarWithUpload size={28} style={{ marginLeft: 8 }} />}
right={
<ActionIcon
icon={Settings2}
onClick={() => {
router.push('/settings');
}}
/>
}
/>
);
return <MobileNavBar center={<Logo type={'text'} />} />;
});
export default Header;

View file

@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
import { Center } from 'react-layout-kit';
import { useGlobalStore } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { agentMarketSelectors, useMarketStore } from '@/store/market';
import { useSessionStore } from '@/store/session';
@ -54,7 +55,7 @@ const Header = memo(() => {
if (!agentItem) return;
createSession({ config, meta });
switchSideBar('chat');
switchSideBar(SidebarTabKey.Chat);
}}
type={'primary'}
>

View file

@ -1,4 +1,5 @@
import { Markdown, TabsNav } from '@lobehub/ui';
import { useResponsive } from 'antd-style';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
@ -22,7 +23,8 @@ const AgentModalInner = memo(() => {
s.useFetchAgent,
s.currentIdentifier,
]);
const { styles } = useStyles();
const { styles, theme } = useStyles();
const { mobile } = useResponsive();
const { data, isLoading } = useFetchAgent(currentIdentifier);
const { t } = useTranslation('market');
const [tab, setTab] = useState<string>(InfoTabs.prompt);
@ -34,7 +36,13 @@ const AgentModalInner = memo(() => {
return (
<>
<AgentCardBanner mask meta={meta} size={10} style={{ height: 120, marginBottom: -60 }} />
<AgentCardBanner
mask
maskColor={mobile ? theme.colorBgContainer : undefined}
meta={meta}
size={10}
style={{ height: 120, marginBottom: -60 }}
/>
<Header />
<Flexbox align={'center'}>
<TabsNav

View file

@ -1,14 +1,10 @@
import { ActionIcon } from '@lobehub/ui';
import { Drawer } from 'antd';
import { useTheme } from 'antd-style';
import { X } from 'lucide-react';
import { Modal } from '@lobehub/ui';
import { PropsWithChildren, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { agentMarketSelectors, useMarketStore } from '@/store/market';
const Mobile = memo<PropsWithChildren>(({ children }) => {
const theme = useTheme();
const [showAgentSidebar, deactivateAgent] = useMarketStore((s) => [
agentMarketSelectors.showSideBar(s),
s.deactivateAgent,
@ -17,19 +13,9 @@ const Mobile = memo<PropsWithChildren>(({ children }) => {
const { t } = useTranslation('market');
return (
<Drawer
bodyStyle={{ padding: 0 }}
closeIcon={<ActionIcon icon={X} size={{ blockSize: 32, fontSize: 20 }} />}
drawerStyle={{ background: theme.colorBgLayout }}
headerStyle={{ padding: '8px 4px' }}
height={'75vh'}
onClose={deactivateAgent}
open={showAgentSidebar}
placement={'bottom'}
title={t('sidebar.title')}
>
<Modal onCancel={deactivateAgent} open={showAgentSidebar} title={t('sidebar.title')}>
{children}
</Drawer>
</Modal>
);
});

View file

@ -6,6 +6,7 @@ import { Center, Flexbox } from 'react-layout-kit';
import SafeSpacing from '@/components/SafeSpacing';
import { MAX_WIDTH } from '@/const/layoutTokens';
import { useSwitchSideBarOnInit } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import AppLayout from '../../layout/AppLayout';
import Header from './features/Header';
@ -27,7 +28,7 @@ const useStyles = createStyles(({ css }) => ({
const MarketLayout = memo<PropsWithChildren>(({ children }) => {
const { theme, styles } = useStyles();
useSwitchSideBarOnInit('market');
useSwitchSideBarOnInit(SidebarTabKey.Market);
return (
<AppLayout>

View file

@ -3,12 +3,13 @@ import { Flexbox } from 'react-layout-kit';
import AppMobileLayout from '@/layout/AppMobileLayout';
import { useSwitchSideBarOnInit } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import Header from './features/Header';
import SideBar from './features/SideBar';
const MarketLayout = memo<{ children: ReactNode }>(({ children }) => {
useSwitchSideBarOnInit('market');
useSwitchSideBarOnInit(SidebarTabKey.Market);
return (
<AppMobileLayout navBar={<Header />} showTabBar>

View file

@ -0,0 +1,20 @@
'use client';
import { memo } from 'react';
import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
import { SettingsTabs } from '@/store/global/initialState';
import Layout from '../index';
import Agent from './Agent';
const AgentSetting = memo(() => {
useSwitchSideBarOnInit(SettingsTabs.Agent);
return (
<Layout>
<Agent />
</Layout>
);
});
export default AgentSetting;

View file

@ -0,0 +1,7 @@
import Page from './index';
const Index = () => {
return <Page />;
};
export default Index;

View file

@ -15,7 +15,7 @@ import { useSessionStore } from '@/store/session';
import { ConfigKeys } from '@/types/settings';
import { switchLang } from '@/utils/switchLang';
import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from '../ThemeSwatches';
import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from '../features/ThemeSwatches';
type SettingItemGroup = ItemGroup & {
children: {

View file

@ -0,0 +1,20 @@
'use client';
import { memo } from 'react';
import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
import { SettingsTabs } from '@/store/global/initialState';
import Layout from '../index';
import Common from './Common';
const CommonSetting = memo(() => {
useSwitchSideBarOnInit(SettingsTabs.Common);
return (
<Layout>
<Common />
</Layout>
);
});
export default CommonSetting;

View file

@ -0,0 +1,7 @@
import Page from './index';
const Index = () => {
return <Page />;
};
export default Index;

View file

@ -1,94 +0,0 @@
import { GridBackground, Icon, Logo } from '@lobehub/ui';
import { createStyles, useResponsive } from 'antd-style';
import { PackageCheck } from 'lucide-react';
import { rgba } from 'polished';
import { ReactNode, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import pkg from '@/../package.json';
const useStyles = createStyles(({ css, token, isDarkMode, prefixCls }) => ({
background: css`
position: absolute;
bottom: -10%;
left: 0;
width: 100%;
`,
banner: css`
position: relative;
overflow: hidden;
width: 100%;
max-width: 1024px;
height: 160px;
padding: 4px;
background: radial-gradient(
120% 120% at 20% 100%,
${isDarkMode ? rgba(token.colorBgContainer, 0.5) : token.colorBgContainer} 32%,
${isDarkMode ? token.colorPrimaryBgHover : rgba(token.colorPrimaryBgHover, 0.3)} 50%,
${isDarkMode ? token.colorPrimaryHover : rgba(token.colorPrimaryHover, 0.3)} 100%
);
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusLG}px;
`,
logo: css`
position: absolute;
top: 50%;
left: 32px;
transform: translateY(-50%);
`,
mobile: css`
margin-top: -16px;
.${prefixCls}-tabs-tab, .${prefixCls}-tabs-tab + .${prefixCls}-tabs-tab {
margin: 4px 8px !important;
}
`,
tag: css`
position: absolute;
top: 6px;
right: 12px;
font-family: ${token.fontFamilyCode};
font-size: 12px;
color: ${isDarkMode ? token.colorPrimaryBg : token.colorPrimaryActive};
opacity: 0.5;
`,
}));
const Banner = memo<{ nav: ReactNode }>(({ nav }) => {
const { styles, theme } = useStyles();
const { mobile } = useResponsive();
if (mobile)
return (
<Flexbox align={'flex-end'} className={styles.mobile} horizontal justify={'center'}>
{nav}
</Flexbox>
);
return (
<Flexbox align={'flex-end'} className={styles.banner} horizontal justify={'center'}>
<GridBackground
animation
className={styles.background}
colorBack={theme.colorFillSecondary}
colorFront={theme.colorPrimary}
random
/>
{nav}
<div className={styles.logo}>
<Logo extra={'Chat'} type={'text'} />
</div>
<Flexbox align={'center'} className={styles.tag} gap={4} horizontal>
<Icon icon={PackageCheck} />
<div>{`${pkg.version}`}</div>
</Flexbox>
</Flexbox>
);
});
export default Banner;

View file

@ -1,13 +1,20 @@
import { ChatHeader, ChatHeaderTitle, Logo } from '@lobehub/ui';
import { ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/store/global';
const Index = memo(() => {
const { t } = useTranslation('setting');
const tab = useGlobalStore((s) => s.settingsTab);
return (
<ChatHeader
left={<ChatHeaderTitle title={<Logo extra={t('header.global')} type={'text'} />} />}
left={
<div style={{ paddingLeft: 8 }}>
<ChatHeaderTitle title={t(`tab.${tab}`)} />
</div>
}
/>
);
});

View file

@ -3,14 +3,17 @@ import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/store/global';
const Header = memo(() => {
const { t } = useTranslation('setting');
const tab = useGlobalStore((s) => s.settingsTab);
const router = useRouter();
return (
<MobileNavBar
center={<MobileNavBarTitle title={t('header.global')} />}
onBackClick={() => router.push('/chat')}
center={<MobileNavBarTitle title={t(`tab.${tab}`)} />}
onBackClick={() => router.back()}
showBackButton
/>
);

View file

@ -1,53 +0,0 @@
import { TabsNav } from '@lobehub/ui';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Center } from 'react-layout-kit';
import { useGlobalStore } from '@/store/global';
import Banner from '../Banner';
import Agent from './Agent';
import Common from './Common';
import LLM from './LLM';
const Settings = memo(() => {
const { t } = useTranslation('setting');
const [tab, setTab] = useGlobalStore((s) => [s.settingsTab, s.switchSettingTabs]);
const content = useMemo(() => {
switch (tab) {
case 'llm': {
return <LLM />;
}
case 'agent': {
return <Agent />;
}
default:
case 'common': {
return <Common />;
}
}
}, [tab]);
return (
<Center gap={16} width={'100%'}>
<Banner
nav={
<TabsNav
activeKey={tab}
items={[
{ key: 'common', label: t('tab.common') },
{ key: 'llm', label: t('tab.llm') },
{ key: 'agent', label: t('tab.agent') },
]}
onChange={(e) => setTab(e as any)}
/>
}
/>
{content}
</Center>
);
});
export default Settings;

View file

@ -0,0 +1,39 @@
import { Icon, List } from '@lobehub/ui';
import { createStyles, useResponsive } from 'antd-style';
import { ChevronRight, type LucideIcon } from 'lucide-react';
import { CSSProperties, ReactNode, memo } from 'react';
const { Item } = List;
const useStyles = createStyles(({ css }) => ({
container: css`
padding-top: 20px;
padding-bottom: 20px;
`,
}));
export interface ItemProps {
active?: boolean;
className?: string;
icon: LucideIcon;
label: ReactNode;
style?: CSSProperties;
}
const SettingItem = memo<ItemProps>(({ label, icon, active = false, style, className }) => {
const { cx, styles } = useStyles();
const { mobile } = useResponsive();
return (
<Item
active={active}
avatar={<Icon icon={icon} size={{ fontSize: 16 }} />}
className={cx(styles.container, className)}
style={style}
title={label as string}
>
{mobile && <Icon icon={ChevronRight} size={{ fontSize: 16 }} />}
</Item>
);
});
export default SettingItem;

View file

@ -0,0 +1,30 @@
import { useResponsive } from 'antd-style';
import { Bot, Settings2, Webhook } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/store/global';
import { SettingsTabs } from '@/store/global/initialState';
import Item from './Item';
const List = memo(() => {
const { t } = useTranslation('setting');
const tab = useGlobalStore((s) => s.settingsTab);
const router = useRouter();
const { mobile } = useResponsive();
const items = [
{ icon: Settings2, label: t('tab.common'), value: SettingsTabs.Common },
{ icon: Webhook, label: t('tab.llm'), value: SettingsTabs.LLM },
{ icon: Bot, label: t('tab.agent'), value: SettingsTabs.Agent },
];
return items.map(({ value, icon, label }) => (
<div key={value} onClick={() => router.push(`/settings/${value}`)}>
<Item active={mobile ? false : tab === value} icon={icon} label={label} />
</div>
));
});
export default List;

View file

@ -0,0 +1,38 @@
import { DraggablePanelBody, Logo } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import FolderPanel from '@/features/FolderPanel';
import List from './List';
const useStyles = createStyles(({ stylish, token, css }) => ({
body: stylish.noScrollbar,
logo: css`
fill: ${token.colorText};
`,
top: css`
position: sticky;
top: 0;
`,
}));
const SideBar = memo(() => {
const { styles } = useStyles();
return (
<FolderPanel>
<DraggablePanelBody className={styles.body} style={{ padding: 0 }}>
<Flexbox className={styles.top} padding={16}>
<div>
<Logo className={styles.logo} size={36} type={'text'} />
</div>
</Flexbox>
<List />
</DraggablePanelBody>
</FolderPanel>
);
});
export default SideBar;

View file

@ -2,33 +2,31 @@
import { useResponsive } from 'antd-style';
import Head from 'next/head';
import { memo } from 'react';
import { ReactNode, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useSwitchSideBarOnInit } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { genSiteHeadTitle } from '@/utils/genSiteHeadTitle';
import Settings from './features/Settings';
import DesktopLayout from './layout.desktop';
import MobileLayout from './layout.mobile';
const Setting = memo(() => {
const Setting = memo<{ children: ReactNode }>(({ children }) => {
const { mobile } = useResponsive();
const { t } = useTranslation('setting');
const pageTitle = genSiteHeadTitle(t('header.global'));
const RenderLayout = mobile ? MobileLayout : DesktopLayout;
useSwitchSideBarOnInit('settings');
useSwitchSideBarOnInit(SidebarTabKey.Setting);
return (
<>
<Head>
<title>{pageTitle}</title>
</Head>
<RenderLayout>
<Settings />
</RenderLayout>
<RenderLayout>{children}</RenderLayout>
</>
);
});

View file

@ -1,19 +1,23 @@
import { ReactNode, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import { Center, Flexbox } from 'react-layout-kit';
import SafeSpacing from '@/components/SafeSpacing';
import AppLayout from '@/layout/AppLayout';
import Header from './features/Header';
import SideBar from './features/SideBar';
const SettingLayout = memo<{ children: ReactNode }>(({ children }) => {
return (
<AppLayout>
<SideBar />
<Flexbox flex={1} height={'100vh'} style={{ position: 'relative' }}>
<Header />
<Flexbox align={'center'} flex={1} padding={24} style={{ overflow: 'auto' }}>
<SafeSpacing />
{children}
<Center gap={16} width={'100%'}>
{children}
</Center>
</Flexbox>
</Flexbox>
</AppLayout>

View file

@ -8,9 +8,7 @@ import Header from './features/Header';
const SettingLayout = memo<{ children: ReactNode }>(({ children }) => {
return (
<AppMobileLayout navBar={<Header />}>
<Flexbox align={'center'} padding={16} style={{ overflow: 'auto' }}>
{children}
</Flexbox>
<Flexbox style={{ overflow: 'auto' }}>{children}</Flexbox>
</AppMobileLayout>
);
});

View file

@ -1,4 +1,4 @@
import { Form, Markdown } from '@lobehub/ui';
import { Form, type ItemGroup, Markdown } from '@lobehub/ui';
import { Form as AntForm, AutoComplete, Input, Switch } from 'antd';
import { createStyles } from 'antd-style';
import { debounce } from 'lodash-es';
@ -49,7 +49,7 @@ const LLM = memo(() => {
const useAzure = useGlobalStore((s) => s.settings.languageModel.openAI.useAzure);
const openAI = {
const openAI: ItemGroup = {
children: [
{
children: (
@ -94,6 +94,7 @@ const LLM = memo(() => {
),
desc: t('llm.OpenAI.useAzure.desc'),
label: t('llm.OpenAI.useAzure.title'),
minWidth: undefined,
name: [configKey, 'openAI', 'useAzure'],
valuePropName: 'checked',
},
@ -124,6 +125,7 @@ const LLM = memo(() => {
children: <Checker checkModel={!useAzure} />,
desc: t('llm.OpenAI.check.desc'),
label: t('llm.OpenAI.check.title'),
minWidth: undefined,
},
// {
// children: useAzure ? <Flexbox>{t('llm.OpenAI.models.notSupport')}</Flexbox> : <ModelList />,

View file

@ -0,0 +1,20 @@
'use client';
import { memo } from 'react';
import { useSwitchSideBarOnInit } from '@/store/global/hooks/useSwitchSettingsOnInit';
import { SettingsTabs } from '@/store/global/initialState';
import Layout from '../index';
import LLM from './LLM';
const LLMSetting = memo(() => {
useSwitchSideBarOnInit(SettingsTabs.LLM);
return (
<Layout>
<LLM />
</Layout>
);
});
export default LLMSetting;

View file

@ -0,0 +1,7 @@
import Page from './index';
const Index = () => {
return <Page />;
};
export default Index;

View file

@ -0,0 +1,61 @@
import { Upload } from 'antd';
import { useResponsive } from 'antd-style';
import { Feather, FileClock, HardDriveDownload, HardDriveUpload, Heart } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { ABOUT, CHANGELOG, FEEDBACK } from '@/const/url';
import { useExportConfig } from '@/hooks/useExportConfig';
import { useImportConfig } from '@/hooks/useImportConfig';
import { useGlobalStore } from '@/store/global';
import Item from '../features/SideBar/Item';
const ExtraList = memo(() => {
const { t } = useTranslation('common');
const { exportAll } = useExportConfig();
const { importConfig } = useImportConfig();
const tab = useGlobalStore((s) => s.settingsTab);
const { mobile } = useResponsive();
const items = [
{
icon: HardDriveDownload,
label: t('export'),
onClick: exportAll,
value: 'export',
},
{
icon: Feather,
label: t('feedback'),
onClick: () => window.open(FEEDBACK, '__blank'),
value: 'feedback',
},
{
icon: FileClock,
label: t('changelog'),
onClick: () => window.open(CHANGELOG, '__blank'),
value: 'changelog',
},
{
icon: Heart,
label: t('about'),
onClick: () => window.open(ABOUT, '__blank'),
value: 'about',
},
];
return (
<>
<Upload maxCount={1} onChange={importConfig} showUploadList={false}>
<Item icon={HardDriveUpload} label={t('import')} style={{ width: '100vw' }} />
</Upload>
{items.map(({ value, icon, label, onClick }) => (
<div key={value} onClick={onClick}>
<Item active={mobile ? false : tab === value} icon={icon} label={label} />
</div>
))}
</>
);
});
export default ExtraList;

View file

@ -0,0 +1,43 @@
'use client';
import { Divider } from 'antd';
import { memo } from 'react';
import { Center } from 'react-layout-kit';
import AgentCardBanner from '@/app/market/features/AgentCard/AgentCardBanner';
import Header from '@/app/market/features/Header';
import AvatarWithUpload from '@/features/AvatarWithUpload';
import AppMobileLayout from '@/layout/AppMobileLayout';
import { useGlobalStore, useSwitchSideBarOnInit } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { AVATAR } from '@/store/session/slices/chat/actions/share';
import List from '../features/SideBar/List';
import ExtraList from './ExtraList';
const Setting = memo(() => {
const avatar = useGlobalStore((s) => s.settings.avatar);
useSwitchSideBarOnInit(SidebarTabKey.Setting);
return (
<AppMobileLayout navBar={<Header />} showTabBar>
<AgentCardBanner
mask
meta={{ avatar: avatar || AVATAR }}
size={10}
style={{ height: 180, marginBottom: 0 }}
>
<Center style={{ position: 'absolute', zIndex: 2 }}>
<AvatarWithUpload size={100} />
</Center>
</AgentCardBanner>
<div style={{ width: '100%' }}>
<Divider style={{ margin: '0 0 8px' }} />
<List />
<Divider style={{ margin: '8px 0' }} />
<ExtraList />
</div>
</AppMobileLayout>
);
});
export default Setting;

View file

@ -0,0 +1,7 @@
import Page from './index';
const Index = () => {
return <Page />;
};
export default Index;

View file

@ -1,7 +1 @@
import Page from './index';
const Index = () => {
return <Page />;
};
export default Index;
export { default } from './common/page';

View file

@ -0,0 +1,32 @@
import { useResponsive } from 'antd-style';
import { ReactNode, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
interface MobilePaddingProps {
bottom?: number;
children: ReactNode;
gap?: number;
left?: number;
right?: number;
top?: number;
}
const MobilePadding = memo<MobilePaddingProps>(
({ children, top = 16, right = 16, left = 16, bottom = 16, gap }) => {
const { mobile } = useResponsive();
if (mobile)
return (
<Flexbox
gap={gap}
style={{ paddingBottom: bottom, paddingLeft: left, paddingRight: right, paddingTop: top }}
>
{children}
</Flexbox>
);
return children;
},
);
export default MobilePadding;

View file

@ -13,3 +13,4 @@ export const FORM_STYLE: FormProps = {
itemMinWidth: 'max(30%,240px)',
style: { maxWidth: MAX_WIDTH, width: '100%' },
};
export const MOBILE_HEADER_ICON_SIZE = { blockSize: 36, fontSize: 22 };

View file

@ -78,7 +78,6 @@ const AgentMeta = memo(() => {
onChange={(backgroundColor) => updateMeta({ backgroundColor })}
/>
),
divider: false,
label: t('settingAgent.backgroundColor.title'),
minWidth: undefined,
},

View file

@ -1,9 +1,10 @@
import { Input, Modal, Tooltip } from '@lobehub/ui';
import { Form } from 'antd';
import { Form, Input, Modal } from '@lobehub/ui';
import { Alert } from 'antd';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import MobilePadding from '@/components/MobilePadding';
import { PLUGINS_INDEX_URL } from '@/const/url';
interface MarketSettingModalProps {
@ -15,20 +16,32 @@ const MarketSettingModal = memo<MarketSettingModalProps>(({ open, onOpenChange }
const { t } = useTranslation('plugin');
return (
<Modal
footer={null}
onCancel={() => onOpenChange(false)}
open={open}
title={t('settings.title')}
>
<Flexbox gap={12}>
<Form layout={'vertical'}>
<Form.Item extra={t('settings.modalDesc')} label={t('settings.indexUrl.title')}>
<Tooltip title={t('settings.indexUrl.tooltip')}>
<Input defaultValue={PLUGINS_INDEX_URL} disabled placeholder={'https://xxxxx.com'} />
</Tooltip>
</Form.Item>
</Form>
<Modal footer={null} onCancel={() => onOpenChange(false)} open={open} title={t('setting')}>
<Flexbox gap={16}>
<MobilePadding bottom={0} gap={16}>
<Alert message={t('settings.indexUrl.tooltip')} showIcon type={'warning'} />
</MobilePadding>
<Form
items={[
{
children: [
{
children: (
<Input
defaultValue={PLUGINS_INDEX_URL}
disabled
placeholder={PLUGINS_INDEX_URL}
style={{ width: '100%' }}
/>
),
desc: t('settings.modalDesc'),
label: t('settings.indexUrl.title'),
},
],
title: t('settings.title'),
},
]}
/>
</Flexbox>
</Modal>
);

View file

@ -1,12 +1,13 @@
import { Icon, MobileTabBar, type MobileTabBarProps } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { Bot, MessageSquare } from 'lucide-react';
import { Bot, MessageSquare, User } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { rgba } from 'polished';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useGlobalStore } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
const useStyles = createStyles(({ css, token }) => ({
active: css`
@ -27,20 +28,31 @@ export default memo<{ className?: string }>(({ className }) => {
icon: (active) => (
<Icon className={active ? styles.active : undefined} icon={MessageSquare} />
),
key: 'chat',
key: SidebarTabKey.Chat,
onClick: () => {
setTab(SidebarTabKey.Chat);
router.push('/chat');
},
title: t('tab.chat'),
},
{
icon: (active) => <Icon className={active ? styles.active : undefined} icon={Bot} />,
key: 'market',
key: SidebarTabKey.Market,
onClick: () => {
setTab(SidebarTabKey.Market);
router.push('/market');
},
title: t('tab.market'),
},
{
icon: (active) => <Icon className={active ? styles.active : undefined} icon={User} />,
key: SidebarTabKey.Setting,
onClick: () => {
setTab(SidebarTabKey.Setting);
router.push('/settings/mobile');
},
title: t('tab.setting'),
},
],
[t],
);

View file

@ -1,5 +1,5 @@
import { Avatar, Form } from '@lobehub/ui';
import { Form as AForm, Card, FormInstance, Switch, Tag } from 'antd';
import { Form as AForm, FormInstance, Switch, Tag } from 'antd';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
@ -28,9 +28,16 @@ const PluginPreview = memo<{ form: FormInstance }>(({ form }) => {
};
return (
<Card size={'small'} title={t('dev.preview.card')}>
<Form.Item {...items} colon={false} style={{ marginBottom: 0 }} />
</Card>
<Form
colon={false}
items={[
{
children: [items],
title: t('dev.preview.card'),
},
]}
style={{ marginBottom: 0 }}
/>
);
});

View file

@ -1,9 +1,11 @@
import { Modal } from '@lobehub/ui';
import { App, Button, Form, Popconfirm } from 'antd';
import { Alert, App, Button, Form, Popconfirm } from 'antd';
import { useResponsive } from 'antd-style';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';
import MobilePadding from '@/components/MobilePadding';
import { CustomPlugin } from '@/types/plugin';
import ManifestForm from './ManifestForm';
@ -25,7 +27,7 @@ const DevModal = memo<DevModalProps>(
const isEditMode = mode === 'edit';
const { t } = useTranslation('plugin');
const { message } = App.useApp();
const { mobile } = useResponsive();
const [submitting, setSubmitting] = useState(false);
const [form] = Form.useForm();
useEffect(() => {
@ -98,14 +100,16 @@ const DevModal = memo<DevModalProps>(
open={open}
title={t('dev.title')}
>
<Flexbox gap={12}>
{t('dev.modalDesc')}
{/*<Tabs*/}
{/* items={[*/}
{/* { children: <MetaForm />, key: 'meta', label: t('dev.tabs.meta') },*/}
{/* { children: <ManifestForm />, key: 'manifest', label: t('dev.tabs.manifest') },*/}
{/* ]}*/}
{/*/>*/}
<Flexbox gap={mobile ? 0 : 16}>
<MobilePadding bottom={0} gap={16}>
<Alert message={t('dev.modalDesc')} showIcon type={'info'} />
{/*<Tabs*/}
{/* items={[*/}
{/* { children: <MetaForm />, key: 'meta', label: t('dev.tabs.meta') },*/}
{/* { children: <ManifestForm />, key: 'manifest', label: t('dev.tabs.manifest') },*/}
{/* ]}*/}
{/*/>*/}
</MobilePadding>
<PluginPreview form={form} />
<ManifestForm form={form} />
<MetaForm form={form} mode={mode} />

View file

@ -18,6 +18,7 @@ import { ABOUT, CHANGELOG, DISCORD, FEEDBACK, GITHUB } from '@/const/url';
import { useExportConfig } from '@/hooks/useExportConfig';
import { useImportConfig } from '@/hooks/useImportConfig';
import { GlobalStore } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
export interface BottomActionProps {
setTab: GlobalStore['switchSideBar'];
@ -27,7 +28,6 @@ export interface BottomActionProps {
const BottomActions = memo<BottomActionProps>(({ tab, setTab }) => {
const router = useRouter();
const { t } = useTranslation('common');
const { exportSessions, exportSettings, exportAll, exportAgents } = useExportConfig();
const { importConfig } = useImportConfig();
@ -101,7 +101,7 @@ const BottomActions = memo<BottomActionProps>(({ tab, setTab }) => {
key: 'setting',
label: t('setting'),
onClick: () => {
setTab('settings');
setTab(SidebarTabKey.Setting);
router.push('/settings');
},
},
@ -124,7 +124,7 @@ const BottomActions = memo<BottomActionProps>(({ tab, setTab }) => {
title={'Github'}
/>
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
<ActionIcon active={tab === 'settings'} icon={Settings2} />
<ActionIcon active={tab === SidebarTabKey.Setting} icon={Settings2} />
</Dropdown>
</>
);

View file

@ -5,6 +5,7 @@ import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { GlobalStore } from '@/store/global';
import { SidebarTabKey } from '@/store/global/initialState';
import { useSessionStore } from '@/store/session';
export interface TopActionProps {
@ -20,25 +21,25 @@ const TopActions = memo<TopActionProps>(({ tab, setTab }) => {
return (
<>
<ActionIcon
active={tab === 'chat'}
active={tab === SidebarTabKey.Chat}
icon={MessageSquare}
onClick={() => {
// 如果已经在 chat 路径下了,那么就不用再跳转了
if (pathname?.startsWith('/chat')) return;
switchBackToChat();
setTab('chat');
setTab(SidebarTabKey.Chat);
}}
placement={'right'}
size="large"
title={t('tab.chat')}
/>
<ActionIcon
active={tab === 'market'}
active={tab === SidebarTabKey.Market}
icon={Bot}
onClick={() => {
if (pathname?.startsWith('/market')) return;
router.push('/market');
setTab('market');
setTab(SidebarTabKey.Market);
}}
placement={'right'}
size="large"

View file

@ -38,7 +38,7 @@ export default {
import: '导入配置',
inbox: {
defaultMessage:
'你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击<kbd>+</kbd>创建自定义助手',
'你好,我是你的智能助手,你可以问我任何问题,我会尽力回答你。如果需要获得更加专业或定制的助手,可以点击`+`创建自定义助手',
desc: '开启大脑集群,激发思维火花。你的智能助理,在这里与你交流一切',
title: '随便聊聊',
},

View file

@ -96,10 +96,11 @@ export default {
plugins: {
unknown: '插件检测中...',
},
setting: '插件设置',
settings: {
indexUrl: {
title: '市场索引',
tooltip: '暂不支持编辑',
tooltip: '暂不支持在线编辑,请通过部署时环境变量进行设置',
},
modalDesc: '配置插件市场的地址后,可以使用自定义的插件市场',
title: '设置插件市场',

View file

@ -0,0 +1,9 @@
import { SettingsTabs } from '../initialState';
import { useGlobalStore } from '../store';
/**
*
*/
export const useSwitchSideBarOnInit = (key: SettingsTabs) => {
useGlobalStore.getState().switchSettingTabs(key);
};

View file

@ -1,9 +1,17 @@
import { DEFAULT_SETTINGS } from '@/const/settings';
import type { GlobalSettings } from '@/types/settings';
export type SidebarTabKey = 'chat' | 'market' | 'settings';
export enum SidebarTabKey {
Chat = 'chat',
Market = 'market',
Setting = 'settings',
}
export type SettingsTabs = 'agent' | 'common' | 'llm';
export enum SettingsTabs {
Agent = 'agent',
Common = 'common',
LLM = 'llm',
}
export interface Guide {
// Topic 引导
@ -21,7 +29,7 @@ export interface GlobalState {
*
*/
settings: GlobalSettings;
settingsTab?: SettingsTabs;
settingsTab: SettingsTabs;
sidebarKey: SidebarTabKey;
}
@ -44,5 +52,6 @@ export const initialState: GlobalState = {
showSessionPanel: true,
},
settings: DEFAULT_SETTINGS,
sidebarKey: 'chat',
settingsTab: SettingsTabs.Common,
sidebarKey: SidebarTabKey.Chat,
};

View file

@ -69,10 +69,6 @@ const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
dbName: 'LobeHub',
selectors: ['preference', 'settings'],
},
url: {
mode: 'hash',
selectors: [{ settingsTab: 'tab' }],
},
}),
};

File diff suppressed because one or more lines are too long

View file

@ -1,31 +1,7 @@
import { Theme, css } from 'antd-style';
import { readableColor } from 'polished';
export default ({ token, prefixCls }: { prefixCls: string; token: Theme }) => css`
.${prefixCls}-btn {
box-shadow: none;
}
.${prefixCls}-popover {
export default ({ token }: { prefixCls: string; token: Theme }) => css`
.${token.prefixCls}-popover {
z-index: 1100;
}
.${prefixCls}-slider-track, .${prefixCls}-tabs-ink-bar, .${prefixCls}-switch-checked {
background: ${token.colorPrimary} !important;
}
.${prefixCls}-btn-primary:not(.${prefixCls}-btn-dangerous) {
color: ${readableColor(token.colorPrimary)};
background: ${token.colorPrimary};
&:hover {
color: ${readableColor(token.colorPrimary)} !important;
background: ${token.colorPrimaryHover} !important;
}
&:active {
color: ${readableColor(token.colorPrimaryActive)} !important;
background: ${token.colorPrimaryActive} !important;
}
}
`;

View file

@ -20,4 +20,13 @@ export default ({ prefixCls }: { prefixCls: string }) => css`
p {
margin-bottom: 0;
}
@media (max-width: 575px) {
* {
::-webkit-scrollbar {
width: 0;
height: 0;
}
}
}
`;