💄 style: update share style (#11716)

* style: update share style

* style: update share style

* style: update share style

* style: update share style
This commit is contained in:
CanisMinor
2026-01-23 00:39:51 +08:00
committed by GitHub
parent ffad193475
commit 3c70dfacb7
32 changed files with 327 additions and 339 deletions

View File

@@ -3,7 +3,8 @@
import { EDITOR_DEBOUNCE_TIME } from '@lobechat/const';
import { ActionIcon, Flexbox } from '@lobehub/ui';
import { useDebounceFn } from 'ahooks';
import { App, Empty, message } from 'antd';
import { App, message } from 'antd';
import { Empty } from '@lobehub/ui';
import dayjs, { type Dayjs } from 'dayjs';
import { Trash2 } from 'lucide-react';
import { memo, useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';

View File

@@ -1,7 +1,7 @@
'use client';
import { Flexbox } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { Crown, Users } from 'lucide-react';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,7 +17,7 @@ import { systemStatusSelectors } from '@/store/global/selectors';
import AgentBuilderToggle from './AgentBuilderToggle';
import ChromeTabs, { type ChromeTabItem } from './ChromeTabs';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
header: css`
overflow: hidden;
flex: none;
@@ -26,7 +26,7 @@ const useStyles = createStyles(({ css, token }) => ({
height: 44px;
padding-block: 8px;
padding-inline: 12px;
border-block-end: 1px solid ${token.colorBorderSecondary};
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
`,
tabsWrapper: css`
scrollbar-width: none;
@@ -42,7 +42,6 @@ const useStyles = createStyles(({ css, token }) => ({
const Header = memo(() => {
const { t } = useTranslation('chat');
const { styles } = useStyles();
const [showAddModal, setShowAddModal] = useState(false);

View File

@@ -2,7 +2,7 @@
import { getKlavisServerByServerIdentifier, getLobehubSkillProviderById } from '@lobechat/const';
import { Avatar, Flexbox, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { Blocks } from 'lucide-react';
import { type ReactNode, createElement, memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -11,7 +11,7 @@ import SkillStore from '@/features/SkillStore';
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
import { useToolStore } from '@/store/tool';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
banner: css`
cursor: pointer;
@@ -28,24 +28,24 @@ const useStyles = createStyles(({ css, token }) => ({
margin-block-end: 6px;
padding-block: 42px 10px;
padding-inline: 16px;
border: 1px solid ${token.colorBorderSecondary};
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 20px;
background: ${token.colorFillQuaternary};
background: ${cssVar.colorFillQuaternary};
box-shadow: 0 12px 32px rgb(0 0 0 / 4%);
transition: background 0.2s ease-in-out;
&:hover {
background: ${token.colorFillQuaternary};
background: ${cssVar.colorFillQuaternary};
}
`,
icon: css`
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
text: css`
font-size: 13px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
}));
@@ -60,7 +60,6 @@ const BANNER_SKILL_IDS = [
] as const;
const SkillInstallBanner = memo(() => {
const { styles } = useStyles();
const { t } = useTranslation('plugin');
const [open, setOpen] = useState(false);

View File

@@ -1,8 +1,8 @@
'use client';
import { isDesktop } from '@lobechat/const';
import { Flexbox, FormGroup, Text } from '@lobehub/ui';
import { Skeleton as AntSkeleton, Divider } from 'antd';
import { Flexbox, FormGroup, Skeleton, Text } from '@lobehub/ui';
import { Divider } from 'antd';
import { useEffect } from 'react';
import { useTranslation } from 'react-i18next';
@@ -28,20 +28,20 @@ const SkeletonRow = ({ mobile }: { mobile?: boolean }) => {
return (
<Flexbox gap={12} style={rowStyle}>
<Flexbox align="center" horizontal justify="space-between">
<AntSkeleton.Input active size="small" style={{ height: 22, width: 60 }} />
<AntSkeleton.Input active size="small" style={{ height: 22, width: 80 }} />
<Skeleton.Button active size="small" style={{ height: 22, width: 60 }} />
<Skeleton.Button active size="small" style={{ height: 22, width: 80 }} />
</Flexbox>
<AntSkeleton.Input active size="small" style={{ height: 22, width: 120 }} />
<Skeleton.Button active size="small" style={{ height: 22, width: 120 }} />
</Flexbox>
);
}
return (
<Flexbox align="center" gap={24} horizontal justify="space-between" style={rowStyle}>
<Flexbox align="center" gap={24} horizontal style={{ flex: 1 }}>
<AntSkeleton.Input active size="small" style={{ ...labelStyle, height: 22 }} />
<AntSkeleton.Input active size="small" style={{ height: 22, minWidth: 120, width: 160 }} />
<Skeleton.Button active size="small" style={{ ...labelStyle, height: 22 }} />
<Skeleton.Button active size="small" style={{ height: 22, minWidth: 120, width: 160 }} />
</Flexbox>
<AntSkeleton.Input active size="small" style={{ height: 22, width: 100 }} />
<Skeleton.Button active size="small" style={{ height: 22, width: 100 }} />
</Flexbox>
);
};

View File

@@ -394,7 +394,6 @@ const ProviderConfig = memo<ProviderConfigProps>(
),
desc: t('providerModels.config.checker.desc'),
label: t('providerModels.config.checker.title'),
minWidth: undefined,
}
: undefined,
showAceGcm && aceGcmItem,

View File

@@ -64,7 +64,6 @@ const Actions = memo<ActionsProps>(({ identifier, type, isMCP }) => {
setSettingsOpen(true);
}
}}
type="default"
>
{t('store.actions.configure')}
</Button>

View File

@@ -3,7 +3,7 @@
import { type KlavisServerType } from '@lobechat/const';
import { ActionIcon, Avatar, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
import { App, Button } from 'antd';
import { createStyles, cssVar } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { Loader2, MoreVerticalIcon, SquareArrowOutUpRight, Unplug } from 'lucide-react';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,10 +17,10 @@ import { userProfileSelectors } from '@/store/user/selectors';
const POLL_INTERVAL_MS = 1000;
const POLL_TIMEOUT_MS = 15_000;
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
connected: css`
font-size: 14px;
color: ${token.colorSuccess};
color: ${cssVar.colorSuccess};
`,
container: css`
padding-block: 12px;
@@ -28,11 +28,11 @@ const useStyles = createStyles(({ css, token }) => ({
`,
disconnected: css`
font-size: 14px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
error: css`
font-size: 14px;
color: ${token.colorError};
color: ${cssVar.colorError};
`,
icon: css`
display: flex;
@@ -44,20 +44,20 @@ const useStyles = createStyles(({ css, token }) => ({
height: 48px;
border-radius: 12px;
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
`,
pending: css`
font-size: 14px;
color: ${token.colorWarning};
color: ${cssVar.colorWarning};
`,
title: css`
cursor: pointer;
font-size: 15px;
font-weight: 500;
color: ${token.colorText};
color: ${cssVar.colorText};
&:hover {
color: ${token.colorPrimary};
color: ${cssVar.colorPrimary};
}
`,
}));
@@ -69,7 +69,6 @@ interface KlavisSkillItemProps {
const KlavisSkillItem = memo<KlavisSkillItemProps>(({ serverType, server }) => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const { modal } = App.useApp();
const [isConnecting, setIsConnecting] = useState(false);
const [isWaitingAuth, setIsWaitingAuth] = useState(false);

View File

@@ -3,7 +3,7 @@
import { type LobehubSkillProviderType } from '@lobechat/const';
import { ActionIcon, Avatar, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
import { App, Button } from 'antd';
import { createStyles, cssVar } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { Loader2, MoreVerticalIcon, SquareArrowOutUpRight, Unplug } from 'lucide-react';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -18,10 +18,10 @@ import {
const POLL_INTERVAL_MS = 1000;
const POLL_TIMEOUT_MS = 15_000;
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
connected: css`
font-size: 14px;
color: ${token.colorSuccess};
color: ${cssVar.colorSuccess};
`,
container: css`
padding-block: 12px;
@@ -29,17 +29,17 @@ const useStyles = createStyles(({ css, token }) => ({
`,
disconnected: css`
font-size: 14px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
disconnectedIcon: css`
opacity: 0.5;
`,
disconnectedTitle: css`
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
error: css`
font-size: 14px;
color: ${token.colorError};
color: ${cssVar.colorError};
`,
icon: css`
display: flex;
@@ -51,16 +51,16 @@ const useStyles = createStyles(({ css, token }) => ({
height: 48px;
border-radius: 12px;
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
`,
title: css`
cursor: pointer;
font-size: 15px;
font-weight: 500;
color: ${token.colorText};
color: ${cssVar.colorText};
&:hover {
color: ${token.colorPrimary};
color: ${cssVar.colorPrimary};
}
`,
}));
@@ -72,7 +72,6 @@ interface LobehubSkillItemProps {
const LobehubSkillItem = memo<LobehubSkillItemProps>(({ provider, server }) => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const { modal } = App.useApp();
const [isConnecting, setIsConnecting] = useState(false);
const [isWaitingAuth, setIsWaitingAuth] = useState(false);

View File

@@ -1,7 +1,7 @@
'use client';
import { Block, Flexbox, Modal } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -15,7 +15,7 @@ import { type LobeToolType } from '@/types/tool/tool';
import Actions from './Actions';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
padding-block: 12px;
padding-inline: 0;
@@ -33,10 +33,10 @@ const useStyles = createStyles(({ css, token }) => ({
cursor: pointer;
font-size: 15px;
font-weight: 500;
color: ${token.colorText};
color: ${cssVar.colorText};
&:hover {
color: ${token.colorPrimary};
color: ${cssVar.colorPrimary};
}
`,
}));
@@ -52,7 +52,6 @@ interface McpSkillItemProps {
const McpSkillItem = memo<McpSkillItemProps>(
({ identifier, title, avatar, type, runtimeType, author }) => {
const { styles } = useStyles();
const { t } = useTranslation('plugin');
const isMCP = runtimeType === 'mcp';
const isCustomPlugin = type === 'customPlugin';

View File

@@ -11,7 +11,7 @@ import {
getLobehubSkillProviderById,
} from '@lobechat/const';
import { Divider } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -33,7 +33,7 @@ import McpSkillItem from './McpSkillItem';
import KlavisSkillItem from './KlavisSkillItem';
import LobehubSkillItem from './LobehubSkillItem';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
display: flex;
flex-direction: column;
@@ -41,18 +41,17 @@ const useStyles = createStyles(({ css, token }) => ({
`,
description: css`
margin-block-end: 8px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
empty: css`
padding: 24px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
text-align: center;
`,
}));
const SkillList = memo(() => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);
const isKlavisEnabled = useServerConfigStore(serverConfigSelectors.enableKlavis);

View File

@@ -1,6 +1,5 @@
'use client';
import { Flexbox } from '@lobehub/ui';
import { memo, useCallback, useMemo } from 'react';
import { ChatList, ConversationProvider, MessageItem } from '@/features/Conversation';
@@ -44,9 +43,7 @@ const SharedMessageList = memo<SharedMessageListProps>(({ agentId, groupId, shar
replaceMessages(messages, { context: ctx });
}}
>
<Flexbox flex={1}>
<ChatList disableActionsBar itemContent={itemContent} />
</Flexbox>
<ChatList disableActionsBar itemContent={itemContent} />
</ConversationProvider>
);
});

View File

@@ -1,52 +1,27 @@
'use client';
import { Avatar, Flexbox } from '@lobehub/ui';
import { Typography } from 'antd';
import { createStyles, cssVar } from 'antd-style';
import { Alert, Center, Flexbox, Text } from '@lobehub/ui';
import { cx } from 'antd-style';
import NextLink from 'next/link';
import { PropsWithChildren, memo, useEffect, useMemo } from 'react';
import { PropsWithChildren, memo, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { Link, Outlet, useParams } from 'react-router-dom';
import useSWR from 'swr';
import { ProductLogo } from '@/components/Branding';
import { DEFAULT_AVATAR } from '@/const/meta';
import GroupAvatar from '@/features/GroupAvatar';
import UserAvatar from '@/features/User/UserAvatar';
import { useIsDark } from '@/hooks/useIsDark';
import { lambdaClient } from '@/libs/trpc/client';
import { useAgentStore } from '@/store/agent';
import { useUserStore } from '@/store/user';
import { authSelectors } from '@/store/user/slices/auth/selectors';
import SharePortal from '../features/Portal';
const useStyles = createStyles(({ css, token }) => ({
container: css`
width: 100vw;
min-height: 100vh;
background: ${token.colorBgLayout};
`,
content: css`
flex: 1;
width: 100%;
padding-block: 24px;
padding-inline: 24px;
`,
footer: css`
padding-block: 16px;
padding-inline: 24px;
color: ${token.colorTextTertiary};
text-align: center;
`,
header: css`
height: 52px;
padding: 8px;
`,
}));
import { styles } from './style';
const ShareTopicLayout = memo<PropsWithChildren>(({ children }) => {
const { styles } = useStyles();
const { t } = useTranslation('chat');
const isDarkMode = useIsDark();
const { id } = useParams<{ id: string }>();
const dispatchAgentMap = useAgentStore((s) => s.internal_dispatchAgentMap);
const isLogin = useUserStore(authSelectors.isLogin);
@@ -69,100 +44,51 @@ const ShareTopicLayout = memo<PropsWithChildren>(({ children }) => {
}
}, [data?.agentId, data?.agentMeta, dispatchAgentMap]);
const isGroup = !!data?.groupId;
const isInboxAgent = !isGroup && data?.agentMeta?.slug === 'inbox';
const agentOrGroupTitle =
data?.groupMeta?.title || (isInboxAgent ? 'LobeAI' : data?.agentMeta?.title);
const agentMarketIdentifier = data?.agentMeta?.marketIdentifier;
// Build group avatars for GroupAvatar component
const groupAvatars = useMemo(() => {
if (!isGroup || !data?.groupMeta?.members) return [];
return data.groupMeta.members.map((member) => ({
avatar: member.avatar || DEFAULT_AVATAR,
backgroundColor: member.backgroundColor || undefined,
}));
}, [isGroup, data?.groupMeta?.members]);
const renderAgentOrGroupAvatar = () => {
// For group: use GroupAvatar with members
if (isGroup && groupAvatars.length > 0) {
return <GroupAvatar avatars={groupAvatars} size={24} />;
}
// For inbox agent: skip avatar as it's the same as product icon
if (isInboxAgent) {
return null;
}
// For agent: use single Avatar
if (data?.agentMeta?.avatar) {
return (
<Avatar
avatar={data.agentMeta.avatar}
background={data.agentMeta.backgroundColor || cssVar.colorFillTertiary}
shape="square"
size={24}
/>
);
}
return null;
};
const renderAgentOrGroupTitle = () => {
if (!agentOrGroupTitle) return null;
// If agent has marketIdentifier, render as link to assistant page
if (agentMarketIdentifier && !data?.groupMeta?.title) {
return (
<a href={`/community/agent/${agentMarketIdentifier}`} rel="noreferrer" target="_blank">
<Typography.Text ellipsis strong>
{agentOrGroupTitle}
</Typography.Text>
</a>
);
}
return (
<Typography.Text ellipsis strong>
{agentOrGroupTitle}
</Typography.Text>
);
};
return (
<Flexbox className={styles.container}>
<Flexbox align="center" className={styles.header} gap={12} horizontal justify="space-between">
<Flexbox align="center" flex={1} gap={12} horizontal>
{isLogin ? (
<Link to="/">
<ProductLogo size={24} />
</Link>
) : (
<NextLink href="/login">
<ProductLogo size={24} />
</NextLink>
)}
{renderAgentOrGroupAvatar()}
{renderAgentOrGroupTitle()}
<Flexbox className={styles.outerContainer} height={'100%'} padding={8} width={'100%'}>
<Flexbox
className={cx(isDarkMode ? styles.innerContainerDark : styles.innerContainerLight)}
height={'100%'}
width={'100%'}
>
<Flexbox
align={'center'}
gap={8}
horizontal
justify={'space-between'}
padding={8}
width={'100%'}
>
<Flexbox align="center" flex={1} gap={12} horizontal>
{isLogin ? (
<Link to="/">
<UserAvatar size={32} />
</Link>
) : (
<NextLink href="/login">
<ProductLogo size={32} />
</NextLink>
)}
</Flexbox>
<Center flex={2} gap={12} horizontal>
{data?.title && (
<Text align={'center'} ellipsis style={{ textAlign: 'center' }} weight={500}>
{data.title}
</Text>
)}
</Center>
<Flexbox align="center" flex={1} gap={12} horizontal justify={'flex-end'} />
</Flexbox>
{data?.title && (
<Typography.Text ellipsis strong style={{ textAlign: 'center' }}>
{data.title}
</Typography.Text>
)}
<Flexbox align="center" flex={1} horizontal justify="flex-end">
{isLogin && <UserAvatar size={24} />}
<Flexbox className={styles.content} horizontal style={{ overflow: 'hidden' }}>
<Flexbox flex={1} style={{ overflow: 'hidden' }}>
{children ?? <Outlet />}
</Flexbox>
<SharePortal />
</Flexbox>
<Center padding={8} style={{ opacity: 0.25 }}>
<Alert title={t('sharePageDisclaimer')} type={'secondary'} variant={'borderless'} />
</Center>
</Flexbox>
<Flexbox className={styles.content} horizontal style={{ overflow: 'hidden' }}>
<Flexbox flex={1} style={{ overflow: 'hidden' }}>
{children ?? <Outlet />}
</Flexbox>
<SharePortal />
</Flexbox>
<Typography.Text className={styles.footer}>{t('sharePageDisclaimer')}</Typography.Text>
</Flexbox>
);
});

View File

@@ -0,0 +1,59 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
width: 100vw;
min-height: 100vh;
background: ${cssVar.colorBgLayout};
`,
content: css`
flex: 1;
width: 100%;
`,
// Divider 样式
divider: css`
height: 24px;
`,
footer: css`
padding-block: 16px;
padding-inline: 24px;
color: ${cssVar.colorTextTertiary};
text-align: center;
`,
header: css`
height: 52px;
padding: 8px;
`,
// 内层容器 - 深色模式
innerContainerDark: css`
position: relative;
overflow: hidden;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadius};
background: ${cssVar.colorBgContainer};
`,
// 内层容器 - 浅色模式
innerContainerLight: css`
position: relative;
overflow: hidden;
border: 1px solid ${cssVar.colorBorder};
border-radius: ${cssVar.borderRadius};
background: ${cssVar.colorBgContainer};
`,
// 外层容器
outerContainer: css`
position: relative;
`,
}));

View File

@@ -1,7 +1,7 @@
'use client';
import { DraggablePanel } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { CHAT_PORTAL_TOOL_UI_WIDTH } from '@/const/layoutTokens';
@@ -9,7 +9,7 @@ import { PortalContent } from '@/features/Portal/router';
import { useChatStore } from '@/store/chat';
import { chatPortalSelectors } from '@/store/chat/selectors';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ cssVar, css }) => ({
body: css`
overflow: hidden;
display: flex;
@@ -30,17 +30,16 @@ const useStyles = createStyles(({ css, token }) => ({
min-height: 100%;
max-height: 100%;
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
drawer: css`
z-index: 10;
height: 100%;
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
}));
const SharePortal = memo(() => {
const { styles } = useStyles();
const showPortal = useChatStore(chatPortalSelectors.showPortal);
return (

View File

@@ -1,29 +1,27 @@
'use client';
import { Flexbox } from '@lobehub/ui';
import { Button, Center } from '@lobehub/ui';
import { TRPCClientError } from '@trpc/client';
import { Button, Result, Skeleton } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import useSWR from 'swr';
import NotFound from '@/components/404';
import Loading from '@/components/Loading/BrandTextLoading';
import { lambdaClient } from '@/libs/trpc/client';
import SharedMessageList from './SharedMessageList';
const useStyles = createStyles(({ css }) => ({
container: css`
flex: 1;
`,
const styles = createStaticStyles(({ css }) => ({
errorContainer: css`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 400px;
height: 80vh;
padding: 48px;
text-align: center;
@@ -31,7 +29,6 @@ const useStyles = createStyles(({ css }) => ({
}));
const ShareTopicPage = memo(() => {
const { styles } = useStyles();
const { t } = useTranslation('chat');
const { id } = useParams<{ id: string }>();
@@ -41,12 +38,11 @@ const ShareTopicPage = memo(() => {
{ revalidateOnFocus: false },
);
if (isLoading) {
if (!error && isLoading) {
return (
<Flexbox className={styles.container} gap={16}>
<Skeleton active paragraph={{ rows: 1 }} title={false} />
<Skeleton active paragraph={{ rows: 6 }} />
</Flexbox>
<Center className={styles.errorContainer}>
<Loading debugId="share" />
</Center>
);
}
@@ -56,56 +52,53 @@ const ShareTopicPage = memo(() => {
if (errorCode === 'UNAUTHORIZED') {
return (
<Flexbox className={styles.errorContainer}>
<Result
<Center className={styles.errorContainer}>
<NotFound
desc={t('sharePage.error.unauthorized.subtitle')}
extra={
<Button href="/login" type="primary">
{t('sharePage.error.unauthorized.action')}
</Button>
}
status="403"
subTitle={t('sharePage.error.unauthorized.subtitle')}
status={''}
title={t('sharePage.error.unauthorized.title')}
/>
</Flexbox>
</Center>
);
}
if (errorCode === 'FORBIDDEN') {
return (
<Flexbox className={styles.errorContainer}>
<Result
status="403"
subTitle={t('sharePage.error.forbidden.subtitle')}
<Center className={styles.errorContainer}>
<NotFound
desc={t('sharePage.error.forbidden.subtitle')}
status={403}
title={t('sharePage.error.forbidden.title')}
/>
</Flexbox>
</Center>
);
}
// NOT_FOUND or other errors
return (
<Flexbox className={styles.errorContainer}>
<Result
status="404"
subTitle={t('sharePage.error.notFound.subtitle')}
<Center className={styles.errorContainer}>
<NotFound
desc={t('sharePage.error.notFound.subtitle')}
title={t('sharePage.error.notFound.title')}
/>
</Flexbox>
</Center>
);
}
if (!data) return null;
return (
<Flexbox className={styles.container}>
<SharedMessageList
agentId={data.agentId}
groupId={data.groupId}
shareId={data.shareId}
topicId={data.topicId}
/>
</Flexbox>
<SharedMessageList
agentId={data.agentId}
groupId={data.groupId}
shareId={data.shareId}
topicId={data.topicId}
/>
);
});

View File

@@ -1,12 +1,17 @@
'use client';
import { Button, Flexbox, FluentEmoji } from '@lobehub/ui';
import { memo } from 'react';
import { ReactNode, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { MAX_WIDTH } from '@/const/layoutTokens';
const NotFound = memo(() => {
const NotFound = memo<{
desc?: string;
extra?: ReactNode;
status?: number | string;
title?: string;
}>(({ extra, status = 404, title, desc }) => {
const { t } = useTranslation('error');
return (
<Flexbox align={'center'} justify={'center'} style={{ minHeight: '100%', width: '100%' }}>
@@ -21,20 +26,21 @@ const NotFound = memo(() => {
zIndex: 0,
}}
>
404
{status}
</h1>
<FluentEmoji emoji={'👀'} size={64} />
<h2 style={{ fontWeight: 'bold', marginTop: '1em', textAlign: 'center' }}>
{t('notFound.title')}
{title || t('notFound.title')}
</h2>
<div style={{ lineHeight: '1.8', marginBottom: '2em', textAlign: 'center' }}>
<div>{t('notFound.desc')}</div>
<div>{desc || t('notFound.desc')}</div>
<div style={{ marginTop: '0.5em' }}>{t('notFound.check')}</div>
</div>
<Button onClick={() => (window.location.href = '/')} type={'primary'}>
{t('notFound.backHome')}
</Button>
{extra || (
<Button onClick={() => (window.location.href = '/')} type={'primary'}>
{t('notFound.backHome')}
</Button>
)}
</Flexbox>
);
});

View File

@@ -1,6 +1,6 @@
/* eslint-disable no-undef */
import { Center, Flexbox, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { FileImage, FileText, FileUpIcon } from 'lucide-react';
import { memo } from 'react';
import { createPortal } from 'react-dom';
@@ -11,22 +11,22 @@ import { getContainer, useDragUpload } from './useDragUpload';
const BLOCK_SIZE = 64;
const ICON_SIZE = { size: 36, strokeWidth: 1.5 };
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
container: css`
width: 320px;
height: 200px;
padding: calc(${token.borderRadiusLG}px + 4px);
padding: calc(${cssVar.borderRadiusLG} + 4px);
border-radius: 16px;
background: ${token.geekblue};
background: ${cssVar.geekblue};
`,
content: css`
width: 100%;
height: 100%;
padding: 16px;
border: 1.5px dashed #fff;
border-radius: ${token.borderRadiusLG}px;
border-radius: ${cssVar.borderRadiusLG};
`,
desc: css`
font-size: 14px;
@@ -34,24 +34,24 @@ const useStyles = createStyles(({ css, token }) => {
color: #fff;
`,
icon: css`
border-radius: ${token.borderRadiusLG}px;
color: color-mix(in srgb, ${token.geekblue} 95%, black);
background: color-mix(in srgb, ${token.geekblue} 38%, white);
border-radius: ${cssVar.borderRadiusLG};
color: color-mix(in srgb, ${cssVar.geekblue} 95%, black);
background: color-mix(in srgb, ${cssVar.geekblue} 38%, white);
`,
iconGroup: css`
margin-block-start: -44px;
`,
iconLeft: css`
transform: rotateZ(-20deg) translateX(10px);
border-radius: ${token.borderRadiusLG}px;
color: color-mix(in srgb, ${token.geekblue} 95%, black);
background: color-mix(in srgb, ${token.geekblue} 68%, white);
border-radius: ${cssVar.borderRadiusLG};
color: color-mix(in srgb, ${cssVar.geekblue} 95%, black);
background: color-mix(in srgb, ${cssVar.geekblue} 68%, white);
`,
iconRight: css`
transform: rotateZ(20deg) translateX(-10px);
border-radius: ${token.borderRadiusLG}px;
color: color-mix(in srgb, ${token.geekblue} 95%, black);
background: color-mix(in srgb, ${token.geekblue} 68%, white);
border-radius: ${cssVar.borderRadiusLG};
color: color-mix(in srgb, ${cssVar.geekblue} 95%, black);
background: color-mix(in srgb, ${cssVar.geekblue} 68%, white);
`,
title: css`
font-size: 20px;
@@ -66,7 +66,7 @@ const useStyles = createStyles(({ css, token }) => {
width: 100%;
height: 100%;
background: ${token.colorBgMask};
background: ${cssVar.colorBgMask};
transition: all 0.3s ease-in-out;
`,
@@ -80,7 +80,6 @@ interface DragUploadProps {
const DragUpload = memo<DragUploadProps>(({ enabledFiles = true, onUploadFiles }) => {
const { t } = useTranslation('components');
const { styles } = useStyles();
const isDragging = useDragUpload(onUploadFiles);

View File

@@ -1,8 +1,7 @@
'use client';
import { type IEditor } from '@lobehub/editor';
import { Alert } from '@lobehub/ui';
import { Skeleton } from 'antd';
import { Alert, Skeleton } from '@lobehub/ui';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { createStoreUpdater } from 'zustand-utils';

View File

@@ -8,7 +8,7 @@ import {
} from '@lobechat/const';
import { Flexbox, Icon, Image, Modal, Tag, Text, Typography } from '@lobehub/ui';
import { Button, Divider } from 'antd';
import { createStyles, cssVar } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { ExternalLink, Loader2, SquareArrowOutUpRight } from 'lucide-react';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -18,7 +18,7 @@ import { klavisStoreSelectors, lobehubSkillStoreSelectors } from '@/store/tool/s
import { KlavisServerStatus } from '@/store/tool/slices/klavisStore';
import { LobehubSkillStatus } from '@/store/tool/slices/lobehubSkillStore/types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
authorLink: css`
cursor: pointer;
@@ -26,7 +26,7 @@ const useStyles = createStyles(({ css, token }) => ({
gap: 4px;
align-items: center;
color: ${token.colorPrimary};
color: ${cssVar.colorPrimary};
&:hover {
text-decoration: underline;
@@ -39,7 +39,7 @@ const useStyles = createStyles(({ css, token }) => ({
`,
detailLabel: css`
font-size: 12px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
header: css`
display: flex;
@@ -49,7 +49,7 @@ const useStyles = createStyles(({ css, token }) => ({
padding: 16px;
border-radius: 12px;
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
`,
icon: css`
display: flex;
@@ -61,25 +61,25 @@ const useStyles = createStyles(({ css, token }) => ({
height: 56px;
border-radius: 12px;
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
introduction: css`
font-size: 14px;
line-height: 1.8;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
sectionTitle: css`
font-size: 14px;
font-weight: 600;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
title: css`
font-size: 18px;
font-weight: 600;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
toolTag: css`
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 12px;
`,
toolsContainer: css`
@@ -90,7 +90,7 @@ const useStyles = createStyles(({ css, token }) => ({
trustWarning: css`
font-size: 12px;
line-height: 1.6;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
}));
@@ -107,7 +107,6 @@ export interface IntegrationDetailModalProps {
const IntegrationDetailModal = memo<IntegrationDetailModalProps>(
({ open, onClose, type, identifier, isConnecting, onConnect }) => {
const { styles } = useStyles();
const { t } = useTranslation(['plugin', 'setting']);
// Get static config based on type

View File

@@ -2,8 +2,8 @@
import { BRANDING_NAME } from '@lobechat/business-const';
import { Flexbox } from '@lobehub/ui';
import { createStyles, cssVar } from 'antd-style';
import { memo, useEffect } from 'react';
import { createStaticStyles, useTheme } from 'antd-style';
import { memo, useEffect, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useResourceManagerStore } from '@/app/[variants]/(main)/resource/features/store';
@@ -19,7 +19,7 @@ import UploadDock from './components/UploadDock';
const ChunkDrawer = dynamic(() => import('./components/ChunkDrawer'), { ssr: false });
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
container: css`
position: relative;
@@ -33,7 +33,7 @@ const useStyles = createStyles(({ css, token }) => {
width: 100%;
height: 100%;
background-color: ${token.colorBgContainerSecondary};
background-color: var(--editor-overlay-bg, ${cssVar.colorBgContainer});
`,
pageEditorOverlay: css`
position: absolute;
@@ -56,7 +56,7 @@ export type ResouceManagerMode = 'editor' | 'explorer' | 'page';
* Business component, no need be reusable.
*/
const ResourceManager = memo(() => {
const { styles } = useStyles();
const theme = useTheme();
const [, setSearchParams] = useSearchParams();
const [mode, currentViewItemId, libraryId, setMode, setCurrentViewItemId] =
useResourceManagerStore((s) => [
@@ -69,6 +69,13 @@ const ResourceManager = memo(() => {
const currentDocument = useFileStore(documentSelectors.getDocumentById(currentViewItemId));
const cssVariables = useMemo<Record<string, string>>(
() => ({
'--editor-overlay-bg': theme.colorBgContainerSecondary,
}),
[theme.colorBgContainerSecondary],
);
// Fetch specific document when switching to page mode if not already loaded
useEffect(() => {
if (mode === 'page' && currentViewItemId && !currentDocument) {
@@ -98,7 +105,7 @@ const ResourceManager = memo(() => {
return (
<>
<Flexbox className={styles.container} height={'100%'}>
<Flexbox className={styles.container} height={'100%'} style={cssVariables}>
{/* Explorer is always rendered to preserve its state */}
<Explorer />

View File

@@ -1,9 +1,7 @@
import { ModelTag } from '@lobehub/icons';
import { Avatar, Flexbox, Markdown } from '@lobehub/ui';
import { ChatHeaderTitle } from '@lobehub/ui/chat';
import { Avatar, Flexbox, Markdown, Text } from '@lobehub/ui';
import { cx } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { ProductLogo } from '@/components/Branding';
import PluginTag from '@/features/PluginTag';
@@ -19,21 +17,17 @@ import { type FieldType } from './type';
const Preview = memo<FieldType & { title?: string }>(
({ title, withSystemRole, withBackground, withFooter, widthMode }) => {
const [model, plugins, systemRole, isInbox, description, avatar, backgroundColor] =
useAgentStore((s) => [
agentSelectors.currentAgentModel(s),
agentSelectors.displayableAgentPlugins(s),
agentSelectors.currentAgentSystemRole(s),
builtinAgentSelectors.isInboxAgent(s),
agentSelectors.currentAgentDescription(s),
agentSelectors.currentAgentAvatar(s),
agentSelectors.currentAgentBackgroundColor(s),
]);
const { t } = useTranslation('chat');
const [model, plugins, systemRole, isInbox, avatar, backgroundColor] = useAgentStore((s) => [
agentSelectors.currentAgentModel(s),
agentSelectors.displayableAgentPlugins(s),
agentSelectors.currentAgentSystemRole(s),
builtinAgentSelectors.isInboxAgent(s),
agentSelectors.currentAgentDescription(s),
agentSelectors.currentAgentAvatar(s),
agentSelectors.currentAgentBackgroundColor(s),
]);
const displayTitle = isInbox ? 'Lobe AI' : title;
const displayDesc = isInbox ? t('inbox.desc') : description;
return (
<div
@@ -50,24 +44,21 @@ const Preview = memo<FieldType & { title?: string }>(
gap={16}
>
<div className={styles.header}>
<Flexbox align={'flex-start'} gap={12} horizontal>
<Flexbox align={'center'} gap={12} horizontal>
<Avatar
avatar={avatar}
background={backgroundColor}
shape={'square'}
size={40}
size={28}
title={title}
/>
<ChatHeaderTitle
desc={displayDesc}
tag={
<Flexbox gap={4} horizontal>
<ModelTag model={model} />
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
</Flexbox>
}
title={displayTitle}
/>
<Text fontSize={16} strong>
{displayTitle}
</Text>
<Flexbox gap={4} horizontal>
<ModelTag model={model} />
{plugins?.length > 0 && <PluginTag plugins={plugins} />}
</Flexbox>
</Flexbox>
{withSystemRole && systemRole && (
<div className={styles.role}>

View File

@@ -12,7 +12,7 @@ export const styles = createStaticStyles(({ css, cssVar }) => ({
background-size: 120% 120%;
`,
container: css`
background: ${cssVar.colorBgLayout};
background: ${cssVar.colorBgContainer};
`,
container_withBackground_true: css`
overflow: hidden;
@@ -25,7 +25,8 @@ export const styles = createStaticStyles(({ css, cssVar }) => ({
`,
header: css`
margin-block-end: -24px;
padding: 16px;
padding-block: 16px;
padding-inline: 24px;
border-block-end: 1px solid ${cssVar.colorBorder};
background: ${cssVar.colorBgContainer};
`,

View File

@@ -60,7 +60,11 @@ const ShareModal = memo<ModalProps>(({ onCancel, open }) => {
title={t('share', { ns: 'common' })}
width={'min(90vw, 1024px)'}
>
<Flexbox gap={isMobile ? 8 : 24} style={{ overflow: 'hidden', position: 'relative' }}>
<Flexbox
gap={isMobile ? 8 : 24}
height={'100%'}
style={{ overflow: 'hidden', position: 'relative' }}
>
<Segmented
block
onChange={(value) => setTab(value as Tab)}

View File

@@ -5,6 +5,7 @@ export { styles as containerStyles } from './useContainerStyles';
export const styles = createStaticStyles(({ css, cssVar }) => ({
body: css`
height: 100%;
${responsive.sm} {
padding-block-end: 68px;
}

View File

@@ -14,7 +14,7 @@ export const styles = createStaticStyles(({ css, cssVar }) => ({
border: 1px solid ${cssVar.colorBorder};
border-radius: ${cssVar.borderRadiusLG};
background: ${cssVar.colorBgLayout};
background: ${cssVar.colorBgContainer};
/* stylelint-disable selector-class-pattern */
.react-pdf__Document *,

View File

@@ -1,7 +1,15 @@
'use client';
import { Button, Flexbox, Popover, copyToClipboard, usePopoverContext } from '@lobehub/ui';
import { App, Divider, Select, Skeleton, Typography } from 'antd';
import {
Button,
Flexbox,
Popover,
Skeleton,
Text,
copyToClipboard,
usePopoverContext,
} from '@lobehub/ui';
import { App, Divider, Select } from 'antd';
import { ExternalLinkIcon, LinkIcon, LockIcon } from 'lucide-react';
import { type ReactNode, memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -12,7 +20,7 @@ import { useIsMobile } from '@/hooks/useIsMobile';
import { topicService } from '@/services/topic';
import { useChatStore } from '@/store/chat';
import { useStyles } from './style';
import { styles } from './style';
type Visibility = 'private' | 'link';
@@ -23,7 +31,6 @@ interface SharePopoverContentProps {
const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal }) => {
const { t } = useTranslation('chat');
const { message, modal } = App.useApp();
const { styles } = useStyles();
const [updating, setUpdating] = useState(false);
const { close } = usePopoverContext();
const containerRef = useRef<HTMLDivElement>(null);
@@ -103,7 +110,7 @@ const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal }) =>
if (isLoading || !shareInfo) {
return (
<Flexbox className={styles.container} gap={16}>
<Typography.Text strong>{t('share', { ns: 'common' })}</Typography.Text>
<Text strong>{t('share', { ns: 'common' })}</Text>
<Skeleton active paragraph={{ rows: 2 }} />
</Flexbox>
);
@@ -135,10 +142,10 @@ const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal }) =>
return (
<Flexbox className={styles.container} gap={12} ref={containerRef}>
<Typography.Text strong>{t('shareModal.popover.title')}</Typography.Text>
<Text strong>{t('shareModal.popover.title')}</Text>
<Flexbox gap={4}>
<Typography.Text type="secondary">{t('shareModal.popover.visibility')}</Typography.Text>
<Text type="secondary">{t('shareModal.popover.visibility')}</Text>
<Select
disabled={updating}
getPopupContainer={() => containerRef.current || document.body}
@@ -164,9 +171,9 @@ const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal }) =>
/>
</Flexbox>
<Typography.Text className={styles.hint} type="secondary">
<Text className={styles.hint} type="secondary">
{getVisibilityHint()}
</Typography.Text>
</Text>
<Divider style={{ margin: '4px 0' }} />

View File

@@ -1,6 +1,6 @@
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
export const useStyles = createStyles(({ css }) => ({
export const styles = createStaticStyles(({ css }) => ({
container: css`
padding: 16px;
`,

View File

@@ -17,10 +17,10 @@ import { useToolStore } from '@/store/tool';
import { mcpStoreSelectors, pluginSelectors } from '@/store/tool/selectors';
import { type DiscoverMcpItem } from '@/types/discover';
import { useItemStyles } from '../style';
import { itemStyles } from '../style';
const Item = memo<DiscoverMcpItem>(({ name, description, icon, identifier }) => {
const { styles } = useItemStyles();
const styles = itemStyles;
const { t } = useTranslation('plugin');
const { modal } = App.useApp();
const [detailOpen, setDetailOpen] = useState(false);

View File

@@ -8,7 +8,7 @@ import { Loader2, MoreVerticalIcon, Plus, Unplug } from 'lucide-react';
import React, { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { useItemStyles } from '../style';
import { itemStyles } from '../style';
import { useSkillConnect } from './useSkillConnect';
interface ItemProps {
@@ -25,7 +25,7 @@ interface ItemProps {
const Item = memo<ItemProps>(
({ description, icon, identifier, label, onOpenDetail, serverName, type }) => {
const { t } = useTranslation('setting');
const { styles } = useItemStyles();
const styles = itemStyles;
const { modal } = App.useApp();
const { handleConnect, handleDisconnect, isConnected, isConnecting } = useSkillConnect({

View File

@@ -1,7 +1,7 @@
'use client';
import { KLAVIS_SERVER_TYPES, LOBEHUB_SKILL_PROVIDERS } from '@lobechat/const';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
import type { Klavis } from 'klavis';
import { memo, useMemo, useState } from 'react';
@@ -17,7 +17,7 @@ import Empty from '../Empty';
import Item from './Item';
import { useSkillConnect } from './useSkillConnect';
const useStyles = createStyles(({ css }) => ({
const styles = createStaticStyles(({ css }) => ({
grid: css`
display: grid;
grid-template-columns: repeat(2, 1fr);
@@ -69,7 +69,6 @@ const DetailModalWithConnect = memo<DetailModalWithConnectProps>(({ detailState,
DetailModalWithConnect.displayName = 'DetailModalWithConnect';
export const LobeHubList = memo<LobeHubListProps>(({ keywords }) => {
const { styles } = useStyles();
const [detailState, setDetailState] = useState<DetailState | null>(null);
const isLobehubSkillEnabled = useServerConfigStore(serverConfigSelectors.enableLobehubSkill);

View File

@@ -1,6 +1,6 @@
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
export const useItemStyles = createStyles(({ css, token }) => ({
export const itemStyles = createStaticStyles(({ css, cssVar }) => ({
container: css`
position: relative;
overflow: hidden;
@@ -11,7 +11,7 @@ export const useItemStyles = createStyles(({ css, token }) => ({
overflow: hidden;
font-size: 12px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
text-overflow: ellipsis;
white-space: nowrap;
`,
@@ -20,7 +20,7 @@ export const useItemStyles = createStyles(({ css, token }) => ({
font-size: 14px;
font-weight: 500;
color: ${token.colorText};
color: ${cssVar.colorText};
text-overflow: ellipsis;
white-space: nowrap;
`,

View File

@@ -1,9 +1,9 @@
'use client';
import { Button, Flexbox, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, useTheme } from 'antd-style';
import { TriangleAlert, X } from 'lucide-react';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { MANUAL_UPGRADE_URL } from '@/const/url';
@@ -12,7 +12,7 @@ import { useElectronStore } from '@/store/electron';
import { electronSyncSelectors } from '@/store/electron/selectors';
import { useGlobalStore } from '@/store/global';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
closeButton: css`
cursor: pointer;
@@ -26,15 +26,15 @@ const useStyles = createStyles(({ css, token }) => ({
width: 28px;
height: 28px;
border-radius: ${token.borderRadius}px;
border-radius: ${cssVar.borderRadius};
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
transition: all 0.2s;
&:hover {
color: ${token.colorText};
background: ${token.colorFillSecondary};
color: ${cssVar.colorText};
background: ${cssVar.colorFillSecondary};
}
`,
container: css`
@@ -46,7 +46,7 @@ const useStyles = createStyles(({ css, token }) => ({
align-items: center;
justify-content: center;
background: ${token.colorBgMask};
background: ${cssVar.colorBgMask};
`,
content: css`
position: relative;
@@ -55,47 +55,55 @@ const useStyles = createStyles(({ css, token }) => ({
max-width: 480px;
padding: 24px;
border: 1px solid ${token.yellowBorder};
border-radius: ${token.borderRadiusLG}px;
border: 1px solid var(--content-yellow-border, ${cssVar.colorWarningBorder});
border-radius: ${cssVar.borderRadiusLG};
background: ${token.colorBgContainer};
box-shadow: ${token.boxShadowSecondary};
background: ${cssVar.colorBgContainer};
box-shadow: ${cssVar.boxShadowSecondary};
`,
desc: css`
line-height: 1.6;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
font-size: 16px;
font-weight: bold;
color: ${token.colorWarningText};
color: ${cssVar.colorWarningText};
`,
titleIcon: css`
flex-shrink: 0;
color: ${token.colorWarning};
color: ${cssVar.colorWarning};
`,
warning: css`
padding: 12px;
border-radius: ${token.borderRadius}px;
color: ${token.colorWarningText};
background: ${token.yellowBg};
border-radius: ${cssVar.borderRadius};
color: ${cssVar.colorWarningText};
background: var(--warning-yellow-bg, ${cssVar.colorWarningBg});
`,
}));
const ServerVersionOutdatedAlert = () => {
const { styles } = useStyles();
const theme = useTheme();
const { t } = useTranslation('common');
const [dismissed, setDismissed] = useState(false);
const isServerVersionOutdated = useGlobalStore((s) => s.isServerVersionOutdated);
const storageMode = useElectronStore(electronSyncSelectors.storageMode);
const cssVariables = useMemo<Record<string, string>>(
() => ({
'--content-yellow-border': theme.yellowBorder,
'--warning-yellow-bg': theme.yellowBg,
}),
[theme.yellowBorder, theme.yellowBg],
);
// Only show alert when using self-hosted server, not cloud
if (storageMode !== 'selfHost') return null;
if (!isServerVersionOutdated || dismissed) return null;
return (
<div className={styles.container}>
<div className={styles.content}>
<div className={styles.content} style={cssVariables}>
<div className={styles.closeButton} onClick={() => setDismissed(true)}>
<Icon icon={X} />
</div>