mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 11:40:07 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 565dfcf2e5 |
@@ -5,6 +5,14 @@ import { Result } from 'antd';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
minWidth: 240,
|
||||
},
|
||||
});
|
||||
|
||||
type CallbackStatus = 'loading' | 'success' | 'error';
|
||||
|
||||
/**
|
||||
@@ -146,7 +154,7 @@ const MarketAuthCallbackPage = () => {
|
||||
const getExtra = () => {
|
||||
if (status === 'error') {
|
||||
return (
|
||||
<Button block onClick={() => window.close()} size={'large'} style={{ minWidth: 240 }}>
|
||||
<Button block onClick={() => window.close()} size={'large'} style={styles.style}>
|
||||
{t('callback.buttons.close')}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -2,11 +2,19 @@
|
||||
|
||||
import { Button, Flexbox, FluentEmoji, Highlighter, Text } from '@lobehub/ui';
|
||||
import { Result } from 'antd';
|
||||
import Link from '@/libs/next/Link';
|
||||
import { parseAsString, useQueryState } from 'nuqs';
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Link from '@/libs/next/Link';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
minWidth: 240,
|
||||
},
|
||||
});
|
||||
|
||||
const FailedPage = memo(() => {
|
||||
const { t } = useTranslation('oauth');
|
||||
const [reason] = useQueryState('reason');
|
||||
@@ -16,7 +24,7 @@ const FailedPage = memo(() => {
|
||||
<Result
|
||||
extra={
|
||||
<Link href="/public">
|
||||
<Button block size={'large'} style={{ minWidth: 240 }}>
|
||||
<Button block size={'large'} style={styles.style}>
|
||||
{t('error.backToHome')}
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@@ -6,6 +6,13 @@ import { memo, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import BrandLoading from '@/components/Loading/BrandTextLoading';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
display: {
|
||||
display: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
interface BuiltinConsentProps {
|
||||
uid: string;
|
||||
@@ -27,7 +34,7 @@ const BuiltinConsent = memo<BuiltinConsentProps>(({ uid }) => {
|
||||
status="success"
|
||||
title={<Text fontSize={14}>{t('consent.redirecting')}</Text>}
|
||||
/>
|
||||
<form action="/oidc/consent" method="post" ref={formRef} style={{ display: 'none' }}>
|
||||
<form action="/oidc/consent" method="post" ref={formRef} style={styles.display}>
|
||||
<input name="uid" type="hidden" value={uid} />
|
||||
<input name="consent" type="hidden" value="accept" />
|
||||
</form>
|
||||
|
||||
@@ -5,10 +5,20 @@ import React, { memo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import AuthCard from '@/features/AuthCard';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import OAuthApplicationLogo from '../components/OAuthApplicationLogo';
|
||||
import BuiltinConsent from './BuiltinConsent';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
spacing: {
|
||||
marginTop: 8,
|
||||
},
|
||||
});
|
||||
|
||||
interface ClientProps {
|
||||
clientId: string;
|
||||
clientMetadata: {
|
||||
@@ -51,7 +61,7 @@ const ConsentClient = memo<ClientProps>(({ uid, clientId, scopes, clientMetadata
|
||||
/>
|
||||
<AuthCard
|
||||
footer={
|
||||
<form action="/oidc/consent" method="post" style={{ width: '100%' }}>
|
||||
<form action="/oidc/consent" method="post" style={styles.fullWidth}>
|
||||
<input name="uid" type="hidden" value={uid} />
|
||||
<Flexbox gap={12}>
|
||||
<Button
|
||||
@@ -79,7 +89,7 @@ const ConsentClient = memo<ClientProps>(({ uid, clientId, scopes, clientMetadata
|
||||
<Text fontSize={16} type={'secondary'}>
|
||||
{t('consent.permissionsTitle')}
|
||||
</Text>
|
||||
<Flexbox gap={4} style={{ marginTop: 8 }} width={'100%'}>
|
||||
<Flexbox gap={4} style={styles.spacing} width={'100%'}>
|
||||
{scopes.map((scope) => (
|
||||
<Block key={scope} padding={16} variant={'filled'}>
|
||||
<Text>{getScopeDescription(scope, t)}</Text>
|
||||
|
||||
@@ -7,9 +7,16 @@ import { useTranslation } from 'react-i18next';
|
||||
import AuthCard from '@/features/AuthCard';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { userProfileSelectors } from '@/store/user/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import OAuthApplicationLogo from './components/OAuthApplicationLogo';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
interface LoginConfirmProps {
|
||||
clientMetadata: {
|
||||
clientName?: string;
|
||||
@@ -47,7 +54,7 @@ const LoginConfirmClient = memo<LoginConfirmProps>(({ uid, clientMetadata }) =>
|
||||
action="/oidc/consent"
|
||||
method="post"
|
||||
onSubmit={() => setIsLoading(true)}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
>
|
||||
{/* Adjust action URL */}
|
||||
<input name="uid" type="hidden" value={uid} />
|
||||
|
||||
@@ -3,8 +3,19 @@ import { Form } from 'antd';
|
||||
import { Lock } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useResetPassword } from './useResetPassword';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
marginInline: 6,
|
||||
},
|
||||
spacing1: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
});
|
||||
|
||||
interface ResetPasswordContentProps {
|
||||
email: string | null;
|
||||
onSuccessRedirect: (url: string) => void;
|
||||
@@ -45,14 +56,7 @@ export const ResetPasswordContent = ({
|
||||
>
|
||||
<InputPassword
|
||||
placeholder={t('betterAuth.resetPassword.newPasswordPlaceholder')}
|
||||
prefix={
|
||||
<Icon
|
||||
icon={Lock}
|
||||
style={{
|
||||
marginInline: 6,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
prefix={<Icon icon={Lock} style={styles.spacing} />}
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -71,18 +75,11 @@ export const ResetPasswordContent = ({
|
||||
>
|
||||
<InputPassword
|
||||
placeholder={t('betterAuth.resetPassword.confirmPasswordPlaceholder')}
|
||||
prefix={
|
||||
<Icon
|
||||
icon={Lock}
|
||||
style={{
|
||||
marginInline: 6,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
prefix={<Icon icon={Lock} style={styles.spacing} />}
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item style={{ marginBottom: 0 }}>
|
||||
<Form.Item style={styles.spacing1}>
|
||||
<Button block htmlType="submit" loading={loading} size="large" type="primary">
|
||||
{t('betterAuth.resetPassword.submit')}
|
||||
</Button>
|
||||
|
||||
@@ -8,9 +8,32 @@ import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import AuthIcons from '@/components/NextAuth/AuthIcons';
|
||||
import { PRIVACY_URL, TERMS_URL } from '@/const/url';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import AuthCard from '../../../../features/AuthCard';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
spacing: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
spacing1: {
|
||||
marginInline: 6,
|
||||
},
|
||||
spacing2: {
|
||||
padding: 6,
|
||||
},
|
||||
style: {
|
||||
left: 12,
|
||||
position: 'absolute',
|
||||
top: 13,
|
||||
},
|
||||
});
|
||||
|
||||
export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
export const USERNAME_REGEX = /^\w+$/;
|
||||
|
||||
@@ -62,18 +85,12 @@ export const SignInEmailStep = ({
|
||||
<Trans
|
||||
components={{
|
||||
privacy: (
|
||||
<a
|
||||
href={PRIVACY_URL}
|
||||
style={{ color: 'inherit', cursor: 'pointer', textDecoration: 'underline' }}
|
||||
>
|
||||
<a href={PRIVACY_URL} style={styles.colored}>
|
||||
{t('footer.terms')}
|
||||
</a>
|
||||
),
|
||||
terms: (
|
||||
<a
|
||||
href={TERMS_URL}
|
||||
style={{ color: 'inherit', cursor: 'pointer', textDecoration: 'underline' }}
|
||||
>
|
||||
<a href={TERMS_URL} style={styles.colored}>
|
||||
{t('footer.privacy')}
|
||||
</a>
|
||||
),
|
||||
@@ -102,16 +119,7 @@ export const SignInEmailStep = ({
|
||||
{oAuthSSOProviders.map((provider) => (
|
||||
<Button
|
||||
block
|
||||
icon={
|
||||
<Icon
|
||||
icon={AuthIcons(provider, 18)}
|
||||
style={{
|
||||
left: 12,
|
||||
position: 'absolute',
|
||||
top: 13,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
icon={<Icon icon={AuthIcons(provider, 18)} style={styles.style} />}
|
||||
key={provider}
|
||||
loading={socialLoading === provider}
|
||||
onClick={() => onSocialSignIn(provider)}
|
||||
@@ -143,23 +151,14 @@ export const SignInEmailStep = ({
|
||||
},
|
||||
},
|
||||
]}
|
||||
style={{ marginBottom: 0 }}
|
||||
style={styles.spacing}
|
||||
>
|
||||
<Input
|
||||
placeholder={t('betterAuth.signin.emailPlaceholder')}
|
||||
prefix={
|
||||
<Icon
|
||||
icon={Mail}
|
||||
style={{
|
||||
marginInline: 6,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
prefix={<Icon icon={Mail} style={styles.spacing1} />}
|
||||
ref={emailInputRef}
|
||||
size="large"
|
||||
style={{
|
||||
padding: 6,
|
||||
}}
|
||||
style={styles.spacing2}
|
||||
suffix={
|
||||
<Button
|
||||
icon={ChevronRight}
|
||||
|
||||
@@ -6,8 +6,36 @@ import { ChevronLeft, ChevronRight, Lock } from 'lucide-react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import AuthCard from '../../../../features/AuthCard';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
colored1: {
|
||||
color: cssVar.colorPrimary,
|
||||
},
|
||||
spacing: {
|
||||
marginTop: 16,
|
||||
},
|
||||
spacing1: {
|
||||
marginTop: 12,
|
||||
},
|
||||
spacing2: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
spacing3: {
|
||||
marginInline: 6,
|
||||
},
|
||||
spacing4: {
|
||||
padding: 6,
|
||||
},
|
||||
});
|
||||
|
||||
export interface SignInPasswordStepProps {
|
||||
email: string;
|
||||
form: FormInstance<{ password: string }>;
|
||||
@@ -37,19 +65,11 @@ export const SignInPasswordStep = ({
|
||||
footer={
|
||||
<>
|
||||
<Text fontSize={13} type={'secondary'}>
|
||||
<a
|
||||
onClick={onForgotPassword}
|
||||
style={{ color: 'inherit', cursor: 'pointer', textDecoration: 'underline' }}
|
||||
>
|
||||
<a onClick={onForgotPassword} style={styles.colored}>
|
||||
{t('betterAuth.signin.forgotPassword')}
|
||||
</a>
|
||||
</Text>
|
||||
<Button
|
||||
icon={ChevronLeft}
|
||||
onClick={onBackToEmail}
|
||||
size={'large'}
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
<Button icon={ChevronLeft} onClick={onBackToEmail} size={'large'} style={styles.spacing}>
|
||||
{t('betterAuth.signin.backToEmail')}
|
||||
</Button>
|
||||
</>
|
||||
@@ -62,34 +82,25 @@ export const SignInPasswordStep = ({
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onFinish={(values) => onSubmit(values as { password: string })}
|
||||
style={{ marginTop: 12 }}
|
||||
style={styles.spacing1}
|
||||
>
|
||||
<Form.Item
|
||||
name="password"
|
||||
rules={[{ message: t('betterAuth.errors.passwordRequired'), required: true }]}
|
||||
style={{ marginBottom: 0 }}
|
||||
style={styles.spacing2}
|
||||
>
|
||||
<InputPassword
|
||||
placeholder={t('betterAuth.signin.passwordPlaceholder')}
|
||||
prefix={
|
||||
<Icon
|
||||
icon={Lock}
|
||||
style={{
|
||||
marginInline: 6,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
prefix={<Icon icon={Lock} style={styles.spacing3} />}
|
||||
ref={passwordInputRef}
|
||||
size="large"
|
||||
style={{
|
||||
padding: 6,
|
||||
}}
|
||||
style={styles.spacing4}
|
||||
suffix={
|
||||
<Button
|
||||
icon={ChevronRight}
|
||||
loading={loading}
|
||||
onClick={() => form.submit()}
|
||||
style={{ color: cssVar.colorPrimary }}
|
||||
style={styles.colored1}
|
||||
title={t('betterAuth.signin.submit')}
|
||||
variant={'filled'}
|
||||
/>
|
||||
|
||||
@@ -3,14 +3,22 @@
|
||||
import { Button, Icon, Text } from '@lobehub/ui';
|
||||
import { Form, Input } from 'antd';
|
||||
import { Lock, Mail } from 'lucide-react';
|
||||
import Link from '@/libs/next/Link';
|
||||
import { useSearchParams } from '@/libs/next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Link from '@/libs/next/Link';
|
||||
import { useSearchParams } from '@/libs/next/navigation';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { AuthCard } from '../../../../../features/AuthCard';
|
||||
import { type SignUpFormValues, useSignUp } from './useSignUp';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
marginInline: 6,
|
||||
},
|
||||
});
|
||||
|
||||
const BetterAuthSignUpForm = () => {
|
||||
const [form] = Form.useForm<SignUpFormValues>();
|
||||
const { loading, onSubmit, businessElement } = useSignUp();
|
||||
@@ -46,14 +54,7 @@ const BetterAuthSignUpForm = () => {
|
||||
>
|
||||
<Input
|
||||
placeholder={t('betterAuth.signup.emailPlaceholder')}
|
||||
prefix={
|
||||
<Icon
|
||||
icon={Mail}
|
||||
style={{
|
||||
marginInline: 6,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
prefix={<Icon icon={Mail} style={styles.spacing} />}
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -76,14 +77,7 @@ const BetterAuthSignUpForm = () => {
|
||||
>
|
||||
<Input.Password
|
||||
placeholder={t('betterAuth.signup.passwordPlaceholder')}
|
||||
prefix={
|
||||
<Icon
|
||||
icon={Lock}
|
||||
style={{
|
||||
marginInline: 6,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
prefix={<Icon icon={Lock} style={styles.spacing} />}
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -104,14 +98,7 @@ const BetterAuthSignUpForm = () => {
|
||||
>
|
||||
<Input.Password
|
||||
placeholder={t('betterAuth.signup.confirmPasswordPlaceholder')}
|
||||
prefix={
|
||||
<Icon
|
||||
icon={Lock}
|
||||
style={{
|
||||
marginInline: 6,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
prefix={<Icon icon={Lock} style={styles.spacing} />}
|
||||
size="large"
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
+15
-10
@@ -2,6 +2,20 @@ import { Flexbox, type FlexboxProps } from '@lobehub/ui';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { type ReactNode, memo } from 'react';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
background: cssVar.colorBgContainer,
|
||||
bottom: 0,
|
||||
marginTop: 'auto',
|
||||
paddingTop: 16,
|
||||
position: 'sticky',
|
||||
width: '100%',
|
||||
zIndex: 10,
|
||||
},
|
||||
});
|
||||
|
||||
interface OnboardingFooterActionsProps extends Omit<FlexboxProps, 'children'> {
|
||||
left?: ReactNode;
|
||||
right?: ReactNode;
|
||||
@@ -14,16 +28,7 @@ const OnboardingFooterActions = memo<OnboardingFooterActionsProps>(
|
||||
align={'center'}
|
||||
horizontal
|
||||
justify={'space-between'}
|
||||
style={{
|
||||
background: cssVar.colorBgContainer,
|
||||
bottom: 0,
|
||||
marginTop: 'auto',
|
||||
paddingTop: 16,
|
||||
position: 'sticky',
|
||||
width: '100%',
|
||||
zIndex: 10,
|
||||
...style,
|
||||
}}
|
||||
style={StyleSheet.compose(styles.fullWidth, style)}
|
||||
{...rest}
|
||||
>
|
||||
<div>{left}</div>
|
||||
|
||||
@@ -8,10 +8,36 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import LobeMessage from '../components/LobeMessage';
|
||||
import OnboardingFooterActions from '../components/OnboardingFooterActions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored2: {
|
||||
color: cssVar.colorTextDescription,
|
||||
},
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
spacing: {
|
||||
listStyle: 'none',
|
||||
padding: 0,
|
||||
},
|
||||
spacing1: {
|
||||
marginTop: 16,
|
||||
},
|
||||
style: {
|
||||
position: 'absolute',
|
||||
right: 12,
|
||||
top: 12,
|
||||
},
|
||||
style1: {
|
||||
height: '100%',
|
||||
minHeight: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
type DataMode = 'share' | 'privacy';
|
||||
|
||||
interface DataModeStepProps {
|
||||
@@ -38,23 +64,36 @@ const DataModeStep = memo<DataModeStepProps>(({ onBack, onNext }) => {
|
||||
[telemetryEnabled, updateGeneralConfig],
|
||||
);
|
||||
|
||||
// Dynamic styles based on selectedMode
|
||||
const dynamicShareStyle = StyleSheet.create({
|
||||
colored: {
|
||||
borderColor: selectedMode === 'share' ? cssVar.colorSuccess : undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const dynamicPrivacyStyle = StyleSheet.create({
|
||||
colored1: {
|
||||
borderColor: selectedMode === 'privacy' ? cssVar.colorSuccess : undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const checkIcon = (
|
||||
<Checkbox
|
||||
backgroundColor={cssVar.colorSuccess}
|
||||
checked
|
||||
shape={'circle'}
|
||||
size={20}
|
||||
style={{ position: 'absolute', right: 12, top: 12 }}
|
||||
style={styles.style}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Flexbox gap={16} style={{ height: '100%', minHeight: '100%' }}>
|
||||
<Flexbox gap={16} style={styles.style1}>
|
||||
<Flexbox>
|
||||
<LobeMessage sentences={[t('screen4.title'), t('screen4.title2'), t('screen4.title3')]} />
|
||||
<Text as={'p'}>{t('screen4.description')}</Text>
|
||||
</Flexbox>
|
||||
<Flexbox gap={16} style={{ width: '100%' }}>
|
||||
<Flexbox gap={16} style={styles.fullWidth}>
|
||||
{/* 共享数据选项 */}
|
||||
<Block
|
||||
clickable
|
||||
@@ -62,7 +101,7 @@ const DataModeStep = memo<DataModeStepProps>(({ onBack, onNext }) => {
|
||||
gap={16}
|
||||
onClick={() => setMode('share')}
|
||||
padding={16}
|
||||
style={{ borderColor: selectedMode === 'share' ? cssVar.colorSuccess : undefined }}
|
||||
style={StyleSheet.compose(styles.fullWidth, dynamicShareStyle.colored)}
|
||||
variant={'outlined'}
|
||||
>
|
||||
{selectedMode === 'share' && checkIcon}
|
||||
@@ -79,7 +118,7 @@ const DataModeStep = memo<DataModeStepProps>(({ onBack, onNext }) => {
|
||||
}}
|
||||
type={'page'}
|
||||
/>
|
||||
<Flexbox as={'ul'} gap={4} style={{ listStyle: 'none', padding: 0 }}>
|
||||
<Flexbox as={'ul'} gap={4} style={styles.spacing}>
|
||||
<li>
|
||||
<Text>• {t('screen4.share.items.1')}</Text>
|
||||
</li>
|
||||
@@ -99,7 +138,7 @@ const DataModeStep = memo<DataModeStepProps>(({ onBack, onNext }) => {
|
||||
gap={6}
|
||||
onClick={() => setMode('privacy')}
|
||||
padding={16}
|
||||
style={{ borderColor: selectedMode === 'privacy' ? cssVar.colorSuccess : undefined }}
|
||||
style={StyleSheet.compose(styles.fullWidth, dynamicPrivacyStyle.colored1)}
|
||||
variant={'outlined'}
|
||||
>
|
||||
{selectedMode === 'privacy' && checkIcon}
|
||||
@@ -111,17 +150,12 @@ const DataModeStep = memo<DataModeStepProps>(({ onBack, onNext }) => {
|
||||
</Text>
|
||||
</Block>
|
||||
</Flexbox>
|
||||
<Text color={cssVar.colorTextSecondary} fontSize={12} style={{ marginTop: 16 }}>
|
||||
<Text color={cssVar.colorTextSecondary} fontSize={12} style={styles.spacing1}>
|
||||
{t('screen4.footerNote')}
|
||||
</Text>
|
||||
<OnboardingFooterActions
|
||||
left={
|
||||
<Button
|
||||
icon={Undo2Icon}
|
||||
onClick={onBack}
|
||||
style={{ color: cssVar.colorTextDescription }}
|
||||
type={'text'}
|
||||
>
|
||||
<Button icon={Undo2Icon} onClick={onBack} style={styles.colored2} type={'text'}>
|
||||
{t('back')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
@@ -17,9 +17,36 @@ import UserInfo from '@/features/User/UserInfo';
|
||||
import { remoteServerService } from '@/services/electron/remoteServer';
|
||||
import { useElectronStore } from '@/store/electron';
|
||||
import { setDesktopAutoOidcFirstOpenHandled } from '@/utils/electron/autoOidc';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import LobeMessage from '../components/LobeMessage';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
background: cssVar.colorFillSecondary,
|
||||
borderRadius: 8,
|
||||
},
|
||||
colored1: {
|
||||
color: cssVar.colorTextDescription,
|
||||
},
|
||||
colored2: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
},
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
spacing: {
|
||||
marginRight: 4,
|
||||
},
|
||||
spacing1: {
|
||||
marginTop: 32,
|
||||
},
|
||||
style: {
|
||||
height: '100%',
|
||||
minHeight: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
// 登录方式类型
|
||||
type LoginMethod = 'cloud' | 'selfhost';
|
||||
|
||||
@@ -212,19 +239,14 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
const renderCloudContent = () => {
|
||||
if (cloudLoginStatus === 'success') {
|
||||
return (
|
||||
<Flexbox gap={16} style={{ width: '100%' }}>
|
||||
<Flexbox gap={16} style={styles.fullWidth}>
|
||||
<Alert
|
||||
description={t('authResult.success.desc')}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
title={t('authResult.success.title')}
|
||||
type={'success'}
|
||||
/>
|
||||
<UserInfo
|
||||
style={{
|
||||
background: cssVar.colorFillSecondary,
|
||||
borderRadius: 8,
|
||||
}}
|
||||
/>
|
||||
<UserInfo style={styles.colored} />
|
||||
<Button
|
||||
block
|
||||
disabled={isSigningOut || isConnectingServer}
|
||||
@@ -241,7 +263,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
|
||||
if (cloudLoginStatus === 'error') {
|
||||
return (
|
||||
<Flexbox style={{ width: '100%' }}>
|
||||
<Flexbox style={styles.fullWidth}>
|
||||
<Alert
|
||||
description={remoteError || t('authResult.failed.desc')}
|
||||
title={t('authResult.failed.title')}
|
||||
@@ -269,16 +291,16 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
: null;
|
||||
|
||||
return (
|
||||
<Flexbox gap={8} style={{ width: '100%' }}>
|
||||
<Flexbox gap={8} style={styles.fullWidth}>
|
||||
<Button block disabled={true} icon={Cloud} loading={true} size={'large'} type={'primary'}>
|
||||
{t('screen5.actions.signingIn')}
|
||||
</Button>
|
||||
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
||||
<Text style={styles.colored1} type={'secondary'}>
|
||||
{phaseText}
|
||||
</Text>
|
||||
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
||||
{remainingSeconds !== null ? (
|
||||
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
||||
<Text style={styles.colored1} type={'secondary'}>
|
||||
{t('screen5.auth.remaining', {
|
||||
time: remainingSeconds,
|
||||
})}
|
||||
@@ -313,19 +335,14 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
const renderSelfhostContent = () => {
|
||||
if (selfhostLoginStatus === 'success') {
|
||||
return (
|
||||
<Flexbox gap={16} style={{ width: '100%' }}>
|
||||
<Flexbox gap={16} style={styles.fullWidth}>
|
||||
<Alert
|
||||
description={t('authResult.success.desc')}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
title={t('authResult.success.title')}
|
||||
type={'success'}
|
||||
/>
|
||||
<UserInfo
|
||||
style={{
|
||||
background: cssVar.colorFillSecondary,
|
||||
borderRadius: 8,
|
||||
}}
|
||||
/>
|
||||
<UserInfo style={styles.colored} />
|
||||
<Button
|
||||
block
|
||||
disabled={isSigningOut || isConnectingServer}
|
||||
@@ -342,7 +359,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
|
||||
if (selfhostLoginStatus === 'error') {
|
||||
return (
|
||||
<Flexbox gap={16} style={{ width: '100%' }}>
|
||||
<Flexbox gap={16} style={styles.fullWidth}>
|
||||
<Alert
|
||||
description={remoteError || t('authResult.failed.desc')}
|
||||
title={t('authResult.failed.title')}
|
||||
@@ -364,7 +381,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
: null;
|
||||
|
||||
return (
|
||||
<Flexbox gap={8} style={{ width: '100%' }}>
|
||||
<Flexbox gap={8} style={styles.fullWidth}>
|
||||
<Button
|
||||
block
|
||||
disabled={true}
|
||||
@@ -375,12 +392,12 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
>
|
||||
{t('screen5.actions.connecting')}
|
||||
</Button>
|
||||
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
||||
<Text style={styles.colored1} type={'secondary'}>
|
||||
{phaseText}
|
||||
</Text>
|
||||
<Flexbox align={'center'} horizontal justify={'space-between'}>
|
||||
{remainingSeconds !== null ? (
|
||||
<Text style={{ color: cssVar.colorTextDescription }} type={'secondary'}>
|
||||
<Text style={styles.colored1} type={'secondary'}>
|
||||
{t('screen5.auth.remaining', {
|
||||
time: remainingSeconds,
|
||||
})}
|
||||
@@ -397,7 +414,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Flexbox gap={16} style={{ width: '100%' }}>
|
||||
<Flexbox gap={16} style={styles.fullWidth}>
|
||||
<Text color={cssVar.colorTextSecondary}>{t(loginMethodMetas.selfhost.descriptionKey)}</Text>
|
||||
<Input
|
||||
onChange={(e) => setEndpoint(e.target.value)}
|
||||
@@ -413,9 +430,9 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
}
|
||||
}}
|
||||
placeholder={t('screen5.selfhost.endpointPlaceholder')}
|
||||
prefix={<Icon icon={Server} style={{ marginRight: 4 }} />}
|
||||
prefix={<Icon icon={Server} style={styles.spacing} />}
|
||||
size={'large'}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
value={endpoint}
|
||||
/>
|
||||
<Button
|
||||
@@ -423,7 +440,7 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
loading={false}
|
||||
onClick={handleSelfhostConnect}
|
||||
size={'large'}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
type={'primary'}
|
||||
>
|
||||
{t('screen5.actions.connectToServer')}
|
||||
@@ -433,23 +450,17 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Center gap={32} style={{ height: '100%', minHeight: '100%' }}>
|
||||
<Flexbox align={'flex-start'} justify={'flex-start'} style={{ width: '100%' }}>
|
||||
<Center gap={32} style={styles.style}>
|
||||
<Flexbox align={'flex-start'} justify={'flex-start'} style={styles.fullWidth}>
|
||||
<LobeMessage sentences={[t('screen5.title'), t('screen5.title2'), t('screen5.title3')]} />
|
||||
<Text as={'p'}>{t('screen5.description')}</Text>
|
||||
</Flexbox>
|
||||
|
||||
<Flexbox align={'flex-start'} gap={16} style={{ width: '100%' }} width={'100%'}>
|
||||
<Flexbox align={'flex-start'} gap={16} style={styles.fullWidth} width={'100%'}>
|
||||
{renderCloudContent()}
|
||||
{!showEndpoint ? (
|
||||
<Center width={'100%'}>
|
||||
<Button
|
||||
onClick={() => setShowEndpoint(true)}
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
}}
|
||||
type={'text'}
|
||||
>
|
||||
<Button onClick={() => setShowEndpoint(true)} style={styles.colored2} type={'text'}>
|
||||
{t(loginMethodMetas.selfhost.descriptionKey)}
|
||||
</Button>
|
||||
</Center>
|
||||
@@ -466,13 +477,8 @@ const LoginStep = memo<LoginStepProps>(({ onBack, onNext }) => {
|
||||
)}
|
||||
</Flexbox>
|
||||
{canStart() && (
|
||||
<Flexbox horizontal justify={'space-between'} style={{ marginTop: 32 }}>
|
||||
<Button
|
||||
icon={Undo2Icon}
|
||||
onClick={onBack}
|
||||
style={{ color: cssVar.colorTextDescription }}
|
||||
type={'text'}
|
||||
>
|
||||
<Flexbox horizontal justify={'space-between'} style={styles.spacing1}>
|
||||
<Button icon={Undo2Icon} onClick={onBack} style={styles.colored1} type={'text'}>
|
||||
{t('back')}
|
||||
</Button>
|
||||
<Button onClick={onNext} type={'primary'}>
|
||||
|
||||
@@ -16,10 +16,30 @@ import { memo, useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ensureElectronIpc } from '@/utils/electron/ipc';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import LobeMessage from '../components/LobeMessage';
|
||||
import OnboardingFooterActions from '../components/OnboardingFooterActions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored1: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
},
|
||||
colored2: {
|
||||
color: cssVar.colorTextDescription,
|
||||
},
|
||||
flexContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
style: {
|
||||
height: '100%',
|
||||
minHeight: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
type PermissionMeta = {
|
||||
descriptionKey: string;
|
||||
icon: typeof Bell;
|
||||
@@ -155,67 +175,69 @@ const PermissionsStep = memo<PermissionsStepProps>(({ onBack, onNext }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Flexbox gap={16} style={{ height: '100%', minHeight: '100%' }}>
|
||||
<Flexbox gap={16} style={styles.style}>
|
||||
<Flexbox>
|
||||
<LobeMessage sentences={[t('screen3.title'), t('screen3.title2'), t('screen3.title3')]} />
|
||||
<Text as={'p'}>{t('screen3.description')}</Text>
|
||||
</Flexbox>
|
||||
<Block gap={12} padding={4} style={{ width: '100%' }} variant={'outlined'}>
|
||||
{permissions.map((permission) => (
|
||||
<Block
|
||||
align={'center'}
|
||||
clickable={!permission.granted}
|
||||
gap={16}
|
||||
horizontal
|
||||
key={permission.id}
|
||||
onClick={() => !permission.granted && handlePermissionRequest(permission.id)}
|
||||
paddingBlock={8}
|
||||
paddingInline={'12px 12px'}
|
||||
style={{
|
||||
background: permission.granted ? cssVar.colorFillSecondary : undefined,
|
||||
borderColor: permission.granted ? cssVar.colorSuccess : undefined,
|
||||
}}
|
||||
variant={'borderless'}
|
||||
>
|
||||
<Block align={'center'} height={40} justify={'center'} variant={'outlined'} width={40}>
|
||||
<Icon color={cssVar.colorTextDescription} icon={permission.icon} size={20} />
|
||||
</Block>
|
||||
<Flexbox gap={2} style={{ flex: 1 }}>
|
||||
<Text weight={500}>{t(permission.titleKey as any)}</Text>
|
||||
<Text color={cssVar.colorTextSecondary} fontSize={12}>
|
||||
{t(permission.descriptionKey as any)}
|
||||
</Text>
|
||||
</Flexbox>
|
||||
{permission.granted ? (
|
||||
<Icon color={cssVar.colorSuccess} icon={Check} size={20} />
|
||||
) : (
|
||||
<Button
|
||||
icon={SquareArrowOutUpRight}
|
||||
iconPosition={'end'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePermissionRequest(permission.id);
|
||||
}}
|
||||
size={'small'}
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
}}
|
||||
type={'text'}
|
||||
<Block gap={12} padding={4} style={styles.fullWidth} variant={'outlined'}>
|
||||
{permissions.map((permission) => {
|
||||
const dynamicColoredStyle = {
|
||||
background: permission.granted ? cssVar.colorFillSecondary : undefined,
|
||||
borderColor: permission.granted ? cssVar.colorSuccess : undefined,
|
||||
};
|
||||
return (
|
||||
<Block
|
||||
align={'center'}
|
||||
clickable={!permission.granted}
|
||||
gap={16}
|
||||
horizontal
|
||||
key={permission.id}
|
||||
onClick={() => !permission.granted && handlePermissionRequest(permission.id)}
|
||||
paddingBlock={8}
|
||||
paddingInline={'12px 12px'}
|
||||
style={dynamicColoredStyle}
|
||||
variant={'borderless'}
|
||||
>
|
||||
<Block
|
||||
align={'center'}
|
||||
height={40}
|
||||
justify={'center'}
|
||||
variant={'outlined'}
|
||||
width={40}
|
||||
>
|
||||
{t(permission.buttonKey)}
|
||||
</Button>
|
||||
)}
|
||||
</Block>
|
||||
))}
|
||||
<Icon color={cssVar.colorTextDescription} icon={permission.icon} size={20} />
|
||||
</Block>
|
||||
<Flexbox gap={2} style={styles.flexContainer}>
|
||||
<Text weight={500}>{t(permission.titleKey as any)}</Text>
|
||||
<Text color={cssVar.colorTextSecondary} fontSize={12}>
|
||||
{t(permission.descriptionKey as any)}
|
||||
</Text>
|
||||
</Flexbox>
|
||||
{permission.granted ? (
|
||||
<Icon color={cssVar.colorSuccess} icon={Check} size={20} />
|
||||
) : (
|
||||
<Button
|
||||
icon={SquareArrowOutUpRight}
|
||||
iconPosition={'end'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePermissionRequest(permission.id);
|
||||
}}
|
||||
size={'small'}
|
||||
style={styles.colored1}
|
||||
type={'text'}
|
||||
>
|
||||
{t(permission.buttonKey)}
|
||||
</Button>
|
||||
)}
|
||||
</Block>
|
||||
);
|
||||
})}
|
||||
</Block>
|
||||
<OnboardingFooterActions
|
||||
left={
|
||||
<Button
|
||||
icon={Undo2Icon}
|
||||
onClick={onBack}
|
||||
style={{ color: cssVar.colorTextDescription }}
|
||||
type={'text'}
|
||||
>
|
||||
<Button icon={Undo2Icon} onClick={onBack} style={styles.colored2} type={'text'}>
|
||||
{t('back')}
|
||||
</Button>
|
||||
}
|
||||
|
||||
@@ -11,6 +11,17 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ProductLogo } from '@/components/Branding';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
spacing1: {
|
||||
marginBlock: 8,
|
||||
maxWidth: 240,
|
||||
},
|
||||
});
|
||||
|
||||
interface WelcomeStepProps {
|
||||
onNext: () => void;
|
||||
@@ -46,7 +57,7 @@ const WelcomeStep = memo<WelcomeStepProps>(({ onNext }) => {
|
||||
return (
|
||||
<Flexbox gap={16}>
|
||||
<ProductLogo size={64} />
|
||||
<Flexbox style={{ marginBottom: 16 }}>
|
||||
<Flexbox style={styles.spacing}>
|
||||
<Text as={'h1'} fontSize={28} weight={'bold'}>
|
||||
<TypewriterEffect
|
||||
cursorCharacter={<LoadingDots size={28} variant={'pulse'} />}
|
||||
@@ -72,7 +83,7 @@ const WelcomeStep = memo<WelcomeStepProps>(({ onNext }) => {
|
||||
items={[
|
||||
{
|
||||
description: (
|
||||
<Text as={'p'} color={cssVar.colorTextSecondary} style={{ marginBottom: 16 }}>
|
||||
<Text as={'p'} color={cssVar.colorTextSecondary} style={styles.spacing}>
|
||||
{t('telemetry.rows.create.desc')}
|
||||
</Text>
|
||||
),
|
||||
@@ -85,7 +96,7 @@ const WelcomeStep = memo<WelcomeStepProps>(({ onNext }) => {
|
||||
},
|
||||
{
|
||||
description: (
|
||||
<Text as={'p'} color={cssVar.colorTextSecondary} style={{ marginBottom: 16 }}>
|
||||
<Text as={'p'} color={cssVar.colorTextSecondary} style={styles.spacing}>
|
||||
{t('telemetry.rows.collaborate.desc')}
|
||||
</Text>
|
||||
),
|
||||
@@ -111,15 +122,7 @@ const WelcomeStep = memo<WelcomeStepProps>(({ onNext }) => {
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleNext}
|
||||
size={'large'}
|
||||
style={{
|
||||
marginBlock: 8,
|
||||
maxWidth: 240,
|
||||
}}
|
||||
type="primary"
|
||||
>
|
||||
<Button onClick={handleNext} size={'large'} style={styles.spacing1} type="primary">
|
||||
{t('telemetry.next')}
|
||||
</Button>
|
||||
</Flexbox>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import Loading from '@/components/Loading/BrandTextLoading';
|
||||
import { electronSystemService } from '@/services/electron/system';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import OnboardingContainer from './_layout';
|
||||
import DataModeStep from './features/DataModeStep';
|
||||
@@ -21,6 +22,14 @@ import {
|
||||
} from './storage';
|
||||
import { DesktopOnboardingScreen, isDesktopOnboardingScreen } from './types';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
maxWidth: 560,
|
||||
minHeight: '100%',
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
const DesktopOnboardingPage = memo(() => {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [isMac, setIsMac] = useState(true);
|
||||
@@ -206,7 +215,7 @@ const DesktopOnboardingPage = memo(() => {
|
||||
|
||||
return (
|
||||
<OnboardingContainer>
|
||||
<Flexbox gap={24} style={{ maxWidth: 560, minHeight: '100%', width: '100%' }}>
|
||||
<Flexbox gap={24} style={styles.fullWidth}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<Flexbox gap={8}>
|
||||
|
||||
@@ -8,10 +8,26 @@ import { useParams } from 'react-router-dom';
|
||||
|
||||
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
||||
import { useRouter } from '@/libs/router/navigation';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Actions from './Actions';
|
||||
import CronTopicItem from './CronTopicItem';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
spacing: {
|
||||
padding: '8px 12px',
|
||||
},
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
style1: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
});
|
||||
|
||||
interface CronTopicGroupProps {
|
||||
cronJob: AgentCronJob | null;
|
||||
cronJobId: string;
|
||||
@@ -58,9 +74,9 @@ const CronTopicGroup = memo<CronTopicGroupProps>(({ cronJob, cronJobId, topics }
|
||||
paddingBlock={4}
|
||||
paddingInline={'8px 4px'}
|
||||
title={
|
||||
<Flexbox align="center" gap={6} height={24} horizontal style={{ overflow: 'hidden' }}>
|
||||
<Icon icon={isEnabled ? TimerIcon : TimerOffIcon} style={{ opacity: 0.5 }} />
|
||||
<Text ellipsis style={{ flex: 1 }} type={isActive ? undefined : 'secondary'}>
|
||||
<Flexbox align="center" gap={6} height={24} horizontal style={styles.style}>
|
||||
<Icon icon={isEnabled ? TimerIcon : TimerOffIcon} style={styles.style1} />
|
||||
<Text ellipsis style={styles.flexContainer} type={isActive ? undefined : 'secondary'}>
|
||||
{cronJobName}
|
||||
</Text>
|
||||
{topics.length > 0 && (
|
||||
@@ -76,7 +92,7 @@ const CronTopicGroup = memo<CronTopicGroupProps>(({ cronJob, cronJobId, topics }
|
||||
{topics.length > 0 ? (
|
||||
topics.map((topic) => <CronTopicItem key={topic.id} topic={topic} />)
|
||||
) : (
|
||||
<Text fontSize={12} style={{ padding: '8px 12px' }} type="secondary">
|
||||
<Text fontSize={12} style={styles.spacing} type="secondary">
|
||||
{t('agentCronJobs.noExecutionResults')}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
@@ -9,9 +9,20 @@ import { DEFAULT_AVATAR, DEFAULT_INBOX_AVATAR } from '@/const/meta';
|
||||
import { SkeletonItem } from '@/features/NavPanel/components/SkeletonList';
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { agentSelectors, builtinAgentSelectors } from '@/store/agent/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import SwitchPanel from './SwitchPanel';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
minWidth: 32,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
style1: {
|
||||
width: 24,
|
||||
},
|
||||
});
|
||||
|
||||
const Agent = memo<PropsWithChildren>(() => {
|
||||
const { t } = useTranslation(['chat', 'common']);
|
||||
|
||||
@@ -35,10 +46,7 @@ const Agent = memo<PropsWithChildren>(() => {
|
||||
gap={8}
|
||||
horizontal
|
||||
padding={2}
|
||||
style={{
|
||||
minWidth: 32,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
style={styles.style}
|
||||
variant={'borderless'}
|
||||
>
|
||||
<Avatar
|
||||
@@ -56,9 +64,7 @@ const Agent = memo<PropsWithChildren>(() => {
|
||||
blockSize: 28,
|
||||
size: 16,
|
||||
}}
|
||||
style={{
|
||||
width: 24,
|
||||
}}
|
||||
style={styles.style1}
|
||||
/>
|
||||
</Block>
|
||||
</SwitchPanel>
|
||||
|
||||
@@ -9,9 +9,16 @@ import SkeletonList from '@/features/NavPanel/components/SkeletonList';
|
||||
import TopicEmpty from '@/features/TopicEmpty';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { topicSelectors } from '@/store/chat/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import TopicItem from '../List/Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
height: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
const ITEM_HEIGHT = 44; // Each topic item height
|
||||
|
||||
interface ContentProps {
|
||||
@@ -155,7 +162,7 @@ const Content = memo<ContentProps>(({ open, searchKeyword }) => {
|
||||
bufferSize={typeof window !== 'undefined' ? window.innerHeight : 0}
|
||||
onScroll={handleScroll}
|
||||
ref={virtuaRef}
|
||||
style={{ height: '100%' }}
|
||||
style={styles.style}
|
||||
>
|
||||
{activeTopicList?.map((topic) => (
|
||||
<Flexbox gap={1} key={topic.id} padding={'4px 8px'}>
|
||||
|
||||
@@ -9,6 +9,7 @@ import NavItem from '@/features/NavPanel/components/NavItem';
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import ThreadList from '../../TopicListContent/ThreadList';
|
||||
import { useTopicNavigation } from '../../hooks/useTopicNavigation';
|
||||
@@ -16,6 +17,20 @@ import Actions from './Actions';
|
||||
import Editing from './Editing';
|
||||
import { useTopicItemDropdownMenu } from './useDropdownMenu';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: cssVar.colorTextDescription,
|
||||
fontSize: 10,
|
||||
},
|
||||
fullWidth: {
|
||||
height: 18,
|
||||
width: '100%',
|
||||
},
|
||||
style: {
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
interface TopicItemProps {
|
||||
active?: boolean;
|
||||
fav?: boolean;
|
||||
@@ -81,13 +96,7 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
||||
title={
|
||||
<Flexbox align={'center'} flex={1} gap={6} horizontal>
|
||||
{t('defaultTitle')}
|
||||
<Tag
|
||||
size={'small'}
|
||||
style={{
|
||||
color: cssVar.colorTextDescription,
|
||||
fontSize: 10,
|
||||
}}
|
||||
>
|
||||
<Tag size={'small'} style={styles.colored}>
|
||||
{t('temp')}
|
||||
</Tag>
|
||||
</Flexbox>
|
||||
@@ -97,7 +106,7 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
||||
}
|
||||
|
||||
return (
|
||||
<Flexbox style={{ position: 'relative' }}>
|
||||
<Flexbox style={styles.style}>
|
||||
<NavItem
|
||||
actions={<Actions dropdownMenu={dropdownMenu} />}
|
||||
active={active && !threadId && !isInAgentSubRoute}
|
||||
@@ -126,8 +135,8 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
|
||||
<Suspense
|
||||
fallback={
|
||||
<Flexbox gap={8} paddingBlock={8} paddingInline={24} width={'100%'}>
|
||||
<Skeleton.Button active size={'small'} style={{ height: 18, width: '100%' }} />
|
||||
<Skeleton.Button active size={'small'} style={{ height: 18, width: '100%' }} />
|
||||
<Skeleton.Button active size={'small'} style={styles.fullWidth} />
|
||||
<Skeleton.Button active size={'small'} style={styles.fullWidth} />
|
||||
</Flexbox>
|
||||
}
|
||||
>
|
||||
|
||||
+16
-3
@@ -5,9 +5,22 @@ import React, { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { type GroupedTopic } from '@/types/topic';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import TopicItem from '../../List/Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
style1: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
});
|
||||
|
||||
const preformat = (id: string) =>
|
||||
id.startsWith('20') ? (id.includes('-') ? dayjs(id).format('MMMM') : id) : undefined;
|
||||
|
||||
@@ -29,9 +42,9 @@ const GroupItem = memo<GroupItemProps>(({ group, activeTopicId, activeThreadId }
|
||||
paddingBlock={4}
|
||||
paddingInline={'8px 4px'}
|
||||
title={
|
||||
<Flexbox align="center" gap={6} height={24} horizontal style={{ overflow: 'hidden' }}>
|
||||
<Icon icon={HashIcon} style={{ opacity: 0.5 }} />
|
||||
<Text ellipsis fontSize={12} style={{ flex: 1 }} type={'secondary'} weight={500}>
|
||||
<Flexbox align="center" gap={6} height={24} horizontal style={styles.style}>
|
||||
<Icon icon={HashIcon} style={styles.style1} />
|
||||
<Text ellipsis fontSize={12} style={styles.flexContainer} type={'secondary'} weight={500}>
|
||||
{title || timeTitle}
|
||||
</Text>
|
||||
</Flexbox>
|
||||
|
||||
@@ -14,6 +14,24 @@ import { Clock } from 'lucide-react';
|
||||
import { memo, useCallback, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
paddingBottom: 48,
|
||||
},
|
||||
style: {
|
||||
fontWeight: 600,
|
||||
},
|
||||
style1: {
|
||||
borderRadius: 12,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
style2: {
|
||||
minHeight: 220,
|
||||
},
|
||||
});
|
||||
|
||||
interface CronJobContentEditorProps {
|
||||
enableRichRender: boolean;
|
||||
initialValue: string;
|
||||
@@ -70,14 +88,10 @@ const CronJobContentEditor = memo<CronJobContentEditorProps>(
|
||||
<Flexbox gap={12}>
|
||||
<Flexbox align="center" gap={6} horizontal>
|
||||
<Icon icon={Clock} size={16} />
|
||||
<Text style={{ fontWeight: 600 }}>{t('agentCronJobs.content')}</Text>
|
||||
<Text style={styles.style}>{t('agentCronJobs.content')}</Text>
|
||||
</Flexbox>
|
||||
<Card
|
||||
size="small"
|
||||
style={{ borderRadius: 12, overflow: 'hidden' }}
|
||||
styles={{ body: { padding: 0 } }}
|
||||
>
|
||||
<Flexbox padding={16} style={{ minHeight: 220 }}>
|
||||
<Card size="small" style={styles.style1} styles={{ body: { padding: 0 } }}>
|
||||
<Flexbox padding={16} style={styles.style2}>
|
||||
<Editor
|
||||
content={''}
|
||||
editor={editor}
|
||||
@@ -97,7 +111,7 @@ const CronJobContentEditor = memo<CronJobContentEditorProps>(
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
style={{ paddingBottom: 48 }}
|
||||
style={styles.spacing}
|
||||
type={'text'}
|
||||
variant={'chat'}
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,16 @@ import { Switch } from 'antd';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
fontSize: 28,
|
||||
fontWeight: 600,
|
||||
padding: 0,
|
||||
},
|
||||
});
|
||||
|
||||
interface CronJobHeaderProps {
|
||||
enabled?: boolean;
|
||||
isNewJob?: boolean;
|
||||
@@ -21,11 +31,7 @@ const CronJobHeader = memo<CronJobHeaderProps>(
|
||||
<Input
|
||||
onChange={(e) => onNameChange(e.target.value)}
|
||||
placeholder={t('agentCronJobs.form.name.placeholder')}
|
||||
style={{
|
||||
fontSize: 28,
|
||||
fontWeight: 600,
|
||||
padding: 0,
|
||||
}}
|
||||
style={styles.spacing}
|
||||
value={name}
|
||||
variant={'borderless'}
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,14 @@ import { Save } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
width: 200,
|
||||
},
|
||||
});
|
||||
|
||||
interface CronJobSaveButtonProps {
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
@@ -19,7 +27,7 @@ const CronJobSaveButton = memo<CronJobSaveButtonProps>(({ disabled, loading, onS
|
||||
icon={Save}
|
||||
loading={loading}
|
||||
onClick={onSave}
|
||||
style={{ width: 200 }}
|
||||
style={styles.style}
|
||||
type="primary"
|
||||
>
|
||||
{t('agentCronJobs.saveAsNew', { defaultValue: 'Save as New Scheduled Task' })}
|
||||
|
||||
@@ -5,6 +5,8 @@ import dayjs from 'dayjs';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import {
|
||||
SCHEDULE_TYPE_OPTIONS,
|
||||
type ScheduleType,
|
||||
@@ -13,6 +15,31 @@ import {
|
||||
WEEKDAY_OPTIONS,
|
||||
} from '../CronConfig';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
style: {
|
||||
borderRadius: 12,
|
||||
},
|
||||
style1: {
|
||||
minWidth: 120,
|
||||
},
|
||||
style2: {
|
||||
minWidth: 150,
|
||||
},
|
||||
style3: {
|
||||
width: 80,
|
||||
},
|
||||
style4: {
|
||||
maxWidth: 300,
|
||||
minWidth: 200,
|
||||
},
|
||||
style5: {
|
||||
width: 160,
|
||||
},
|
||||
});
|
||||
|
||||
interface CronJobScheduleConfigProps {
|
||||
hourlyInterval?: number;
|
||||
maxExecutions?: number | null;
|
||||
@@ -89,11 +116,11 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
}, [scheduleType, triggerTime, timezone, weekdays, hourlyInterval, t]);
|
||||
|
||||
return (
|
||||
<Card size="small" style={{ borderRadius: 12 }} styles={{ body: { padding: 12 } }}>
|
||||
<Card size="small" style={styles.style} styles={{ body: { padding: 12 } }}>
|
||||
<Flexbox gap={12}>
|
||||
{/* Summary Tags */}
|
||||
{summaryTags.length > 0 && (
|
||||
<Flexbox align="center" gap={8} horizontal style={{ flexWrap: 'wrap' }}>
|
||||
<Flexbox align="center" gap={8} horizontal style={styles.flexContainer}>
|
||||
{summaryTags.map((tag) => (
|
||||
<Tag key={tag.key} variant={'filled'}>
|
||||
{tag.label}
|
||||
@@ -102,7 +129,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
</Flexbox>
|
||||
)}
|
||||
{/* Schedule Configuration - All in one row */}
|
||||
<Flexbox align="center" gap={8} horizontal style={{ flexWrap: 'wrap' }}>
|
||||
<Flexbox align="center" gap={8} horizontal style={styles.flexContainer}>
|
||||
<Tag variant={'borderless'}>{t('agentCronJobs.schedule')}</Tag>
|
||||
<Select
|
||||
onChange={(value: ScheduleType) =>
|
||||
@@ -116,7 +143,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
value: opt.value,
|
||||
}))}
|
||||
size="small"
|
||||
style={{ minWidth: 120 }}
|
||||
style={styles.style1}
|
||||
value={scheduleType}
|
||||
/>
|
||||
|
||||
@@ -129,7 +156,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
options={WEEKDAY_OPTIONS}
|
||||
placeholder="Select days"
|
||||
size="small"
|
||||
style={{ minWidth: 150 }}
|
||||
style={styles.style2}
|
||||
value={weekdays}
|
||||
/>
|
||||
)}
|
||||
@@ -143,7 +170,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
if (value) onScheduleChange({ triggerTime: value });
|
||||
}}
|
||||
size="small"
|
||||
style={{ minWidth: 120 }}
|
||||
style={styles.style1}
|
||||
value={triggerTime ?? dayjs().hour(0).minute(0)}
|
||||
/>
|
||||
)}
|
||||
@@ -159,7 +186,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
onScheduleChange({ hourlyInterval: value ?? 1 })
|
||||
}
|
||||
size="small"
|
||||
style={{ width: 80 }}
|
||||
style={styles.style3}
|
||||
value={hourlyInterval ?? 1}
|
||||
/>
|
||||
<Text type="secondary">hour(s) at</Text>
|
||||
@@ -172,7 +199,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
{ label: ':30', value: 30 },
|
||||
]}
|
||||
size="small"
|
||||
style={{ width: 80 }}
|
||||
style={styles.style3}
|
||||
value={triggerTime?.minute() ?? 0}
|
||||
/>
|
||||
</>
|
||||
@@ -184,13 +211,13 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
options={TIMEZONE_OPTIONS}
|
||||
showSearch
|
||||
size="small"
|
||||
style={{ maxWidth: 300, minWidth: 200 }}
|
||||
style={styles.style4}
|
||||
value={timezone}
|
||||
/>
|
||||
</Flexbox>
|
||||
|
||||
{/* Max Executions */}
|
||||
<Flexbox align="center" gap={8} horizontal style={{ flexWrap: 'wrap' }}>
|
||||
<Flexbox align="center" gap={8} horizontal style={styles.flexContainer}>
|
||||
<Tag variant={'borderless'}>{t('agentCronJobs.maxExecutions')}</Tag>
|
||||
<InputNumber
|
||||
min={1}
|
||||
@@ -199,7 +226,7 @@ const CronJobScheduleConfig = memo<CronJobScheduleConfigProps>(
|
||||
}
|
||||
placeholder={t('agentCronJobs.form.maxExecutions.placeholder')}
|
||||
size="small"
|
||||
style={{ width: 160 }}
|
||||
style={styles.style5}
|
||||
value={maxExecutions ?? null}
|
||||
/>
|
||||
</Flexbox>
|
||||
|
||||
@@ -26,6 +26,7 @@ import { useChatStore } from '@/store/chat';
|
||||
import { serverConfigSelectors, useServerConfigStore } from '@/store/serverConfig';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { labPreferSelectors } from '@/store/user/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { type ScheduleType, buildCronPattern, parseCronPattern } from './CronConfig';
|
||||
import CronJobContentEditor from './features/CronJobContentEditor';
|
||||
@@ -33,6 +34,12 @@ import CronJobHeader from './features/CronJobHeader';
|
||||
import CronJobSaveButton from './features/CronJobSaveButton';
|
||||
import CronJobScheduleConfig from './features/CronJobScheduleConfig';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflowY: 'auto',
|
||||
},
|
||||
});
|
||||
|
||||
interface CronJobDraft {
|
||||
content: string;
|
||||
cronPattern: string;
|
||||
@@ -458,7 +465,7 @@ const CronJobDetailPage = memo(() => {
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
<Flexbox flex={1} style={{ overflowY: 'auto' }}>
|
||||
<Flexbox flex={1} style={styles.style}>
|
||||
<WideScreenContainer paddingBlock={16}>
|
||||
{isLoading && <Loading debugId="CronJobDetailPage" />}
|
||||
{!isLoading && !cronJob && !isNewJob && (
|
||||
|
||||
@@ -5,6 +5,18 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useActionSWR } from '@/libs/swr';
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
height: '20px',
|
||||
justifyContent: 'center',
|
||||
padding: '0 1px',
|
||||
width: '20px',
|
||||
},
|
||||
});
|
||||
|
||||
const AddButton = memo(() => {
|
||||
const navigate = useNavigate();
|
||||
@@ -21,14 +33,7 @@ const AddButton = memo(() => {
|
||||
icon={<Icon icon={Plus} size={'small'} />}
|
||||
loading={isValidating}
|
||||
onClick={() => mutate()}
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
borderRadius: 4,
|
||||
height: '20px',
|
||||
justifyContent: 'center',
|
||||
padding: '0 1px',
|
||||
width: '20px',
|
||||
}}
|
||||
style={styles.spacing}
|
||||
variant={'filled'}
|
||||
/>
|
||||
);
|
||||
|
||||
+26
-11
@@ -20,6 +20,25 @@ import {
|
||||
} from '@/store/tool/slices/klavisStore';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { userProfileSelectors } from '@/store/user/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flex: 'none',
|
||||
},
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
spacing: {
|
||||
marginBlock: 12,
|
||||
},
|
||||
spacing1: {
|
||||
marginTop: 8,
|
||||
},
|
||||
style: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
|
||||
// Tools that require Market authentication
|
||||
const MARKET_AUTH_TOOLS = [
|
||||
@@ -192,7 +211,7 @@ const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete
|
||||
|
||||
const renderIcon = () => {
|
||||
if (typeof tool.icon === 'string') {
|
||||
return <Avatar alt={tool.label} avatar={tool.icon} size={20} style={{ flex: 'none' }} />;
|
||||
return <Avatar alt={tool.label} avatar={tool.icon} size={20} style={styles.flexContainer} />;
|
||||
}
|
||||
return <Icon fill={cssVar.colorText} icon={tool.icon} size={20} />;
|
||||
};
|
||||
@@ -206,9 +225,7 @@ const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete
|
||||
horizontal
|
||||
justify="space-between"
|
||||
onClick={handleAuthorize}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
style={styles.style}
|
||||
>
|
||||
<Flexbox align="center" gap={8} horizontal>
|
||||
{renderIcon()}
|
||||
@@ -253,12 +270,10 @@ const MarketToolAuthItem = memo<MarketToolAuthItemProps>(({ tool }) => {
|
||||
horizontal
|
||||
justify="space-between"
|
||||
onClick={handleSignIn}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
style={styles.style}
|
||||
>
|
||||
<Flexbox align="center" gap={8} horizontal>
|
||||
<Avatar alt={tool.label} avatar={tool.avatar} size={20} style={{ flex: 'none' }} />
|
||||
<Avatar alt={tool.label} avatar={tool.avatar} size={20} style={styles.flexContainer} />
|
||||
<Text>{tool.label}</Text>
|
||||
</Flexbox>
|
||||
<Button
|
||||
@@ -320,8 +335,8 @@ const ToolAuthAlert = memo(() => {
|
||||
description={
|
||||
<>
|
||||
{t('toolAuth.hint')}
|
||||
<Divider dashed style={{ marginBlock: 12 }} />
|
||||
<Flexbox gap={12} style={{ marginTop: 8 }}>
|
||||
<Divider dashed style={styles.spacing} />
|
||||
<Flexbox gap={12} style={styles.spacing1}>
|
||||
{pendingAuthTools.map((tool) =>
|
||||
tool.authType === 'klavis' ? (
|
||||
<KlavisToolAuthItem
|
||||
@@ -339,7 +354,7 @@ const ToolAuthAlert = memo(() => {
|
||||
</>
|
||||
}
|
||||
showIcon={false}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
title={
|
||||
<Flexbox align="center" gap={6} horizontal>
|
||||
{t('toolAuth.title')}
|
||||
|
||||
@@ -11,11 +11,18 @@ import { useAgentStore } from '@/store/agent';
|
||||
import { agentSelectors, builtinAgentSelectors } from '@/store/agent/selectors';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import AddButton from './AddButton';
|
||||
import OpeningQuestions from './OpeningQuestions';
|
||||
import ToolAuthAlert from './ToolAuthAlert';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
paddingBottom: 'max(10vh, 32px)',
|
||||
},
|
||||
});
|
||||
|
||||
const InboxWelcome = memo(() => {
|
||||
const { t } = useTranslation(['welcome', 'chat']);
|
||||
const mobile = useIsMobile();
|
||||
@@ -40,13 +47,7 @@ const InboxWelcome = memo(() => {
|
||||
return (
|
||||
<>
|
||||
<Flexbox flex={1} />
|
||||
<Flexbox
|
||||
gap={12}
|
||||
style={{
|
||||
paddingBottom: 'max(10vh, 32px)',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox gap={12} style={styles.spacing} width={'100%'}>
|
||||
<Avatar
|
||||
avatar={isInbox ? DEFAULT_INBOX_AVATAR : meta.avatar || DEFAULT_AVATAR}
|
||||
background={meta.backgroundColor}
|
||||
|
||||
@@ -9,6 +9,7 @@ import ZenModeToast from '@/features/ZenModeToast';
|
||||
import { useOperationState } from '@/hooks/useOperationState';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { messageMapKey } from '@/store/chat/utils/messageMapKey';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import WelcomeChatItem from './AgentWelcome';
|
||||
import ChatHydration from './ChatHydration';
|
||||
@@ -18,6 +19,14 @@ import ThreadHydration from './ThreadHydration';
|
||||
import { useActionsBarConfig } from './useActionsBarConfig';
|
||||
import { useAgentContext } from './useAgentContext';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* ConversationArea
|
||||
*
|
||||
@@ -54,15 +63,7 @@ const Conversation = memo(() => {
|
||||
operationState={operationState}
|
||||
>
|
||||
<ZenModeToast />
|
||||
<Flexbox
|
||||
flex={1}
|
||||
style={{
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
position: 'relative',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox flex={1} style={styles.style} width={'100%'}>
|
||||
<ChatList welcome={<WelcomeChatItem />} />
|
||||
</Flexbox>
|
||||
<TodoProgress />
|
||||
|
||||
@@ -13,11 +13,18 @@ import { useSessionStore } from '@/store/session';
|
||||
import { sessionSelectors } from '@/store/session/selectors';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { authSelectors } from '@/store/user/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import KnowledgeTag from './KnowledgeTag';
|
||||
import MemberCountTag from './MemberCountTag';
|
||||
import SearchTags from './SearchTags';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
height: 20,
|
||||
},
|
||||
});
|
||||
|
||||
const TitleTags = memo(() => {
|
||||
const [model, provider, hasKnowledge, isLoading] = useAgentStore((s) => [
|
||||
agentSelectors.currentAgentModel(s),
|
||||
@@ -44,7 +51,7 @@ const TitleTags = memo(() => {
|
||||
}
|
||||
|
||||
return isLoading && isLogin ? (
|
||||
<Skeleton.Button active size={'small'} style={{ height: 20 }} />
|
||||
<Skeleton.Button active size={'small'} style={styles.style} />
|
||||
) : (
|
||||
<Flexbox align={'center'} gap={4} horizontal>
|
||||
<ModelSwitchPanel>
|
||||
|
||||
+29
-8
@@ -10,6 +10,27 @@ import { useAgentStore } from '@/store/agent';
|
||||
import { agentByIdSelectors } from '@/store/agent/selectors';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { topicSelectors } from '@/store/chat/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flex: 1,
|
||||
fontSize: 12,
|
||||
},
|
||||
spacing: {
|
||||
margin: 0,
|
||||
},
|
||||
style: {
|
||||
maxWidth: 600,
|
||||
minWidth: 320,
|
||||
},
|
||||
style1: {
|
||||
fontSize: 12,
|
||||
},
|
||||
style2: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
});
|
||||
|
||||
interface WorkingDirectoryContentProps {
|
||||
agentId: string;
|
||||
@@ -91,9 +112,9 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
]);
|
||||
|
||||
return (
|
||||
<Flexbox gap={12} style={{ maxWidth: 600, minWidth: 320 }}>
|
||||
<Flexbox gap={12} style={styles.style}>
|
||||
<Flexbox gap={4}>
|
||||
<Text style={{ fontSize: 12 }} type="secondary">
|
||||
<Text style={styles.style1} type="secondary">
|
||||
{t('localSystem.workingDirectory.agentLevel')}
|
||||
</Text>
|
||||
<Flexbox gap={8} horizontal>
|
||||
@@ -101,7 +122,7 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
onChange={(e) => setAgentDir(e.target.value)}
|
||||
placeholder={t('localSystem.workingDirectory.placeholder')}
|
||||
size="small"
|
||||
style={{ flex: 1, fontSize: 12 }}
|
||||
style={styles.flexContainer}
|
||||
value={agentDir}
|
||||
variant={'filled'}
|
||||
/>
|
||||
@@ -115,7 +136,7 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
|
||||
{activeTopicId && (
|
||||
<>
|
||||
<Divider style={{ margin: 0 }} />
|
||||
<Divider style={styles.spacing} />
|
||||
|
||||
<Flexbox
|
||||
align="center"
|
||||
@@ -124,15 +145,15 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
onClick={() => {
|
||||
setUseTopicOverride(!useTopicOverride);
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
style={styles.style2}
|
||||
>
|
||||
<Switch checked={useTopicOverride} onChange={setUseTopicOverride} size="small" />
|
||||
<Text style={{ fontSize: 12 }}>{t('localSystem.workingDirectory.topicOverride')}</Text>
|
||||
<Text style={styles.style1}>{t('localSystem.workingDirectory.topicOverride')}</Text>
|
||||
</Flexbox>
|
||||
|
||||
{useTopicOverride && (
|
||||
<Flexbox gap={4}>
|
||||
<Text style={{ fontSize: 12 }} type="secondary">
|
||||
<Text style={styles.style1} type="secondary">
|
||||
{t('localSystem.workingDirectory.topicLevel')}
|
||||
</Text>
|
||||
<Flexbox gap={8} horizontal>
|
||||
@@ -140,7 +161,7 @@ const WorkingDirectoryContent = memo<WorkingDirectoryContentProps>(({ agentId, o
|
||||
onChange={(e) => setTopicDir(e.target.value)}
|
||||
placeholder={t('localSystem.workingDirectory.placeholder')}
|
||||
size="small"
|
||||
style={{ flex: 1, fontSize: 12 }}
|
||||
style={styles.flexContainer}
|
||||
value={topicDir}
|
||||
variant={'filled'}
|
||||
/>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
import NavHeader from '@/features/NavHeader';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import HeaderActions from './HeaderActions';
|
||||
import NotebookButton from './NotebookButton';
|
||||
@@ -13,16 +14,22 @@ import ShareButton from './ShareButton';
|
||||
import Tags from './Tags';
|
||||
import WorkingDirectory from './WorkingDirectory';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
backgroundColor: cssVar.colorBgContainer,
|
||||
},
|
||||
});
|
||||
|
||||
const Header = memo(() => {
|
||||
return (
|
||||
<NavHeader
|
||||
left={
|
||||
<Flexbox style={{ backgroundColor: cssVar.colorBgContainer }}>
|
||||
<Flexbox style={styles.colored}>
|
||||
<Tags />
|
||||
</Flexbox>
|
||||
}
|
||||
right={
|
||||
<Flexbox align={'center'} horizontal style={{ backgroundColor: cssVar.colorBgContainer }}>
|
||||
<Flexbox align={'center'} horizontal style={styles.colored}>
|
||||
{isDesktop && <WorkingDirectory />}
|
||||
<NotebookButton />
|
||||
<ShareButton />
|
||||
|
||||
@@ -7,10 +7,18 @@ import { useAgentStore } from '@/store/agent';
|
||||
import { agentSelectors } from '@/store/agent/selectors';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { systemStatusSelectors } from '@/store/global/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import ConversationArea from './ConversationArea';
|
||||
import ChatHeader from './Header';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
const wrapperStyle: React.CSSProperties = {
|
||||
height: '100%',
|
||||
minWidth: 300,
|
||||
@@ -28,11 +36,7 @@ const ChatConversation = memo(() => {
|
||||
return (
|
||||
<Suspense fallback={<Loading debugId="Agent > ChatConversation" />}>
|
||||
<DragUploadZone onUploadFiles={handleUploadFiles} style={wrapperStyle}>
|
||||
<Flexbox
|
||||
height={'100%'}
|
||||
style={{ overflow: 'hidden', position: 'relative' }}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox height={'100%'} style={styles.style} width={'100%'}>
|
||||
{showHeader && <ChatHeader />}
|
||||
<TooltipGroup>
|
||||
<ConversationArea />
|
||||
|
||||
@@ -4,6 +4,16 @@ import { Flexbox } from '@lobehub/ui';
|
||||
import { css, cx } from 'antd-style';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flex: 1,
|
||||
height: 0,
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
const body = css`
|
||||
:has(.portal-artifact) {
|
||||
overflow: hidden;
|
||||
@@ -16,7 +26,7 @@ const Body = ({ children }: PropsWithChildren) => {
|
||||
<Flexbox
|
||||
className={cx(body, 'portal-body')}
|
||||
height={'100%'}
|
||||
style={{ flex: 1, height: 0, position: 'relative' }}
|
||||
style={styles.flexContainer}
|
||||
width={'100%'}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -4,22 +4,25 @@ import { Flexbox } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
|
||||
import MainInterfaceTracker from '@/components/Analytics/MainInterfaceTracker';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Conversation from './features/Conversation';
|
||||
import PageTitle from './features/PageTitle';
|
||||
import Portal from './features/Portal';
|
||||
import TelemetryNotification from './features/TelemetryNotification';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
const ChatPage = memo(() => {
|
||||
return (
|
||||
<>
|
||||
<PageTitle />
|
||||
<Flexbox
|
||||
height={'100%'}
|
||||
horizontal
|
||||
style={{ overflow: 'hidden', position: 'relative' }}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox height={'100%'} horizontal style={styles.style} width={'100%'}>
|
||||
<Conversation />
|
||||
<Portal />
|
||||
</Flexbox>
|
||||
|
||||
@@ -8,9 +8,37 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useAgentCronJobs } from './hooks/useAgentCronJobs';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
display: {
|
||||
WebkitBoxOrient: 'vertical',
|
||||
WebkitLineClamp: 2,
|
||||
color: '#666',
|
||||
display: '-webkit-box',
|
||||
fontSize: '12px',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
flexContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
style: {
|
||||
height: '100%',
|
||||
},
|
||||
style1: {
|
||||
fontSize: '13px',
|
||||
fontWeight: 500,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
style2: {
|
||||
fontSize: '11px',
|
||||
},
|
||||
});
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
interface CronJobCardsProps {
|
||||
@@ -81,7 +109,7 @@ const CronJobCards = memo<CronJobCardsProps>(({ cronJobs, loading, onDelete, onE
|
||||
}
|
||||
loading={loading}
|
||||
size="small"
|
||||
style={{ height: '100%' }}
|
||||
style={styles.style}
|
||||
styles={{
|
||||
actions: { marginTop: 0 },
|
||||
body: { paddingBottom: 12, paddingTop: 8 },
|
||||
@@ -89,18 +117,8 @@ const CronJobCards = memo<CronJobCardsProps>(({ cronJobs, loading, onDelete, onE
|
||||
}}
|
||||
title={
|
||||
<Flexbox align="center" horizontal justify="space-between">
|
||||
<Flexbox align="center" gap={8} horizontal style={{ flex: 1 }}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '13px',
|
||||
fontWeight: 500,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{job.name || t('agentCronJobs.unnamedTask')}
|
||||
</span>
|
||||
<Flexbox align="center" gap={8} horizontal style={styles.flexContainer}>
|
||||
<span style={styles.style1}>{job.name || t('agentCronJobs.unnamedTask')}</span>
|
||||
<Badge status={statusInfo.status} />
|
||||
</Flexbox>
|
||||
<Switch
|
||||
@@ -112,29 +130,19 @@ const CronJobCards = memo<CronJobCardsProps>(({ cronJobs, loading, onDelete, onE
|
||||
}
|
||||
>
|
||||
<Flexbox gap={8}>
|
||||
<Text
|
||||
ellipsis={{ tooltip: job.content }}
|
||||
style={{
|
||||
WebkitBoxOrient: 'vertical',
|
||||
WebkitLineClamp: 2,
|
||||
color: '#666',
|
||||
display: '-webkit-box',
|
||||
fontSize: '12px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Text ellipsis={{ tooltip: job.content }} style={styles.display}>
|
||||
{job.content}
|
||||
</Text>
|
||||
|
||||
<Flexbox gap={8}>
|
||||
<Flexbox align="center" gap={6} horizontal>
|
||||
<Icon icon={Clock} size={12} />
|
||||
<Text style={{ fontSize: '11px' }}>{t(intervalText as any)}</Text>
|
||||
<Text style={styles.style2}>{t(intervalText as any)}</Text>
|
||||
</Flexbox>
|
||||
|
||||
{job.remainingExecutions !== null && (
|
||||
<Flexbox align="center" gap={6} horizontal>
|
||||
<Text style={{ fontSize: '11px' }}>
|
||||
<Text style={styles.style2}>
|
||||
{t('agentCronJobs.remainingExecutions', { count: job.remainingExecutions })}
|
||||
</Text>
|
||||
</Flexbox>
|
||||
@@ -143,7 +151,7 @@ const CronJobCards = memo<CronJobCardsProps>(({ cronJobs, loading, onDelete, onE
|
||||
{job.lastExecutedAt && (
|
||||
<Flexbox align="center" gap={6} horizontal>
|
||||
<Icon icon={Calendar} size={12} />
|
||||
<Text style={{ fontSize: '11px' }}>
|
||||
<Text style={styles.style2}>
|
||||
{dayjs(job.lastExecutedAt).format('MM/DD HH:mm')}
|
||||
</Text>
|
||||
</Flexbox>
|
||||
|
||||
@@ -6,6 +6,13 @@ import { memo, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
// Form data interface - excludes server-managed fields
|
||||
interface CronJobFormData {
|
||||
@@ -167,7 +174,7 @@ const CronJobForm = memo<CronJobFormProps>(({ editingJob, formRef, onSubmit }) =
|
||||
<InputNumber
|
||||
min={1}
|
||||
placeholder={t('agentCronJobs.form.maxExecutions.placeholder')}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -182,7 +189,7 @@ const CronJobForm = memo<CronJobFormProps>(({ editingJob, formRef, onSubmit }) =
|
||||
t('agentCronJobs.form.timeRange.start'),
|
||||
t('agentCronJobs.form.timeRange.end'),
|
||||
]}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -194,7 +201,7 @@ const CronJobForm = memo<CronJobFormProps>(({ editingJob, formRef, onSubmit }) =
|
||||
<InputNumber
|
||||
min={1}
|
||||
placeholder="Leave empty for no daily limit"
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
@@ -8,9 +8,24 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { AgentCronJob } from '@/database/schemas/agentCronJob';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useAgentCronJobs } from './hooks/useAgentCronJobs';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
color: '#666',
|
||||
fontSize: '12px',
|
||||
margin: 0,
|
||||
},
|
||||
spacing1: {
|
||||
marginTop: 4,
|
||||
},
|
||||
style: {
|
||||
fontSize: '11px',
|
||||
},
|
||||
});
|
||||
|
||||
const { Text, Paragraph } = Typography;
|
||||
|
||||
interface CronJobListProps {
|
||||
@@ -91,21 +106,18 @@ const CronJobList = memo<CronJobListProps>(({ cronJobs, loading, onEdit, onDelet
|
||||
}
|
||||
description={
|
||||
<Flexbox gap={4}>
|
||||
<Paragraph
|
||||
ellipsis={{ rows: 2, tooltip: job.content }}
|
||||
style={{ color: '#666', fontSize: '12px', margin: 0 }}
|
||||
>
|
||||
<Paragraph ellipsis={{ rows: 2, tooltip: job.content }} style={styles.spacing}>
|
||||
{job.content}
|
||||
</Paragraph>
|
||||
|
||||
<Flexbox gap={8} horizontal style={{ marginTop: 4 }}>
|
||||
<Flexbox gap={8} horizontal style={styles.spacing1}>
|
||||
<Flexbox align="center" gap={4} horizontal>
|
||||
<Icon icon={Clock} size={12} />
|
||||
<Text style={{ fontSize: '11px' }}>{t(intervalText as any)}</Text>
|
||||
<Text style={styles.style}>{t(intervalText as any)}</Text>
|
||||
</Flexbox>
|
||||
|
||||
{job.remainingExecutions !== null && (
|
||||
<Text style={{ fontSize: '11px' }}>
|
||||
<Text style={styles.style}>
|
||||
{t('agentCronJobs.remainingExecutions', { count: job.remainingExecutions })}
|
||||
</Text>
|
||||
)}
|
||||
@@ -113,7 +125,7 @@ const CronJobList = memo<CronJobListProps>(({ cronJobs, loading, onEdit, onDelet
|
||||
{job.lastExecutedAt && (
|
||||
<Flexbox align="center" gap={4} horizontal>
|
||||
<Icon icon={Calendar} size={12} />
|
||||
<Text style={{ fontSize: '11px' }}>
|
||||
<Text style={styles.style}>
|
||||
{dayjs(job.lastExecutedAt).format('MM/DD HH:mm')}
|
||||
</Text>
|
||||
</Flexbox>
|
||||
|
||||
@@ -10,10 +10,21 @@ import urlJoin from 'url-join';
|
||||
|
||||
import { useQueryRoute } from '@/hooks/useQueryRoute';
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import CronJobCards from './CronJobCards';
|
||||
import { useAgentCronJobs } from './hooks/useAgentCronJobs';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
marginBottom: 16,
|
||||
marginTop: 16,
|
||||
},
|
||||
spacing1: {
|
||||
margin: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
const AgentCronJobs = memo(() => {
|
||||
@@ -54,8 +65,8 @@ const AgentCronJobs = memo(() => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Flexbox gap={12} style={{ marginBottom: 16, marginTop: 16 }}>
|
||||
<Title level={5} style={{ margin: 0 }}>
|
||||
<Flexbox gap={12} style={styles.spacing}>
|
||||
<Title level={5} style={styles.spacing1}>
|
||||
<Flexbox align="center" gap={8} horizontal>
|
||||
<Clock size={16} />
|
||||
{t('agentCronJobs.title')}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { agentSelectors } from '@/store/agent/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useMentionOptions } from '../ProfileEditor/MentionList';
|
||||
import { EMPTY_EDITOR_STATE } from '../constants';
|
||||
@@ -25,6 +26,12 @@ import { useProfileStore } from '../store';
|
||||
import TypoBar from './TypoBar';
|
||||
import { useSlashItems } from './useSlashItems';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
paddingBottom: 64,
|
||||
},
|
||||
});
|
||||
|
||||
const EditorCanvas = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const [editorInit, setEditorInit] = useState(false);
|
||||
@@ -133,9 +140,7 @@ const EditorCanvas = memo(() => {
|
||||
slashOption={{
|
||||
items: slashItems,
|
||||
}}
|
||||
style={{
|
||||
paddingBottom: 64,
|
||||
}}
|
||||
style={styles.spacing}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
+27
-5
@@ -4,6 +4,28 @@ import { Avatar, Flexbox, Modal } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flex: 'none',
|
||||
},
|
||||
spacing: {
|
||||
marginTop: 16,
|
||||
},
|
||||
spacing1: {
|
||||
lineHeight: 1.6,
|
||||
margin: 0,
|
||||
},
|
||||
style: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
style1: {
|
||||
fontSize: 12,
|
||||
opacity: 0.6,
|
||||
},
|
||||
});
|
||||
|
||||
interface OriginalAgentInfo {
|
||||
author?: {
|
||||
avatar?: string;
|
||||
@@ -44,18 +66,18 @@ const ForkConfirmModal = memo<ForkConfirmModalProps>(
|
||||
title={t('marketPublish.forkConfirm.title')}
|
||||
width={480}
|
||||
>
|
||||
<Flexbox gap={16} style={{ marginTop: 16 }}>
|
||||
<Flexbox gap={16} style={styles.spacing}>
|
||||
<Flexbox align="center" gap={12} horizontal>
|
||||
<Avatar avatar={originalAgent.avatar} size={48} style={{ flex: 'none' }} />
|
||||
<Avatar avatar={originalAgent.avatar} size={48} style={styles.flexContainer} />
|
||||
<Flexbox gap={4}>
|
||||
<div style={{ fontWeight: 500 }}>{originalAgent.name}</div>
|
||||
<div style={{ fontSize: 12, opacity: 0.6 }}>
|
||||
<div style={styles.style}>{originalAgent.name}</div>
|
||||
<div style={styles.style1}>
|
||||
{t('marketPublish.forkConfirm.by', { author: authorName })}
|
||||
</div>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
|
||||
<p style={{ lineHeight: 1.6, margin: 0 }}>{t('marketPublish.forkConfirm.description')}</p>
|
||||
<p style={styles.spacing1}>{t('marketPublish.forkConfirm.description')}</p>
|
||||
</Flexbox>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
+11
-5
@@ -6,6 +6,16 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
paddingBottom: 32,
|
||||
paddingTop: 48,
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
interface PublishResultModalProps {
|
||||
identifier?: string;
|
||||
onCancel: () => void;
|
||||
@@ -37,11 +47,7 @@ const PublishResultModal = memo<PublishResultModalProps>(({ identifier, onCancel
|
||||
>
|
||||
<Result
|
||||
icon={<FluentEmoji emoji={'🎉'} size={96} type={'anim'} />}
|
||||
style={{
|
||||
paddingBottom: 32,
|
||||
paddingTop: 48,
|
||||
width: '100%',
|
||||
}}
|
||||
style={styles.fullWidth}
|
||||
subTitle={
|
||||
<Text fontSize={14} type={'secondary'}>
|
||||
{t('marketPublish.resultModal.message')}
|
||||
|
||||
@@ -16,6 +16,22 @@ import { agentSelectors } from '@/store/agent/selectors';
|
||||
import { useFileStore } from '@/store/file';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { globalGeneralSelectors } from '@/store/global/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
fontSize: 36,
|
||||
fontWeight: 600,
|
||||
padding: 0,
|
||||
width: '100%',
|
||||
},
|
||||
style: {
|
||||
cursor: 'default',
|
||||
},
|
||||
style1: {
|
||||
height: 38,
|
||||
},
|
||||
});
|
||||
|
||||
const MAX_AVATAR_SIZE = 1024 * 1024; // 1MB limit for server actions
|
||||
|
||||
@@ -94,9 +110,7 @@ const AgentHeader = memo(() => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
paddingBlock={16}
|
||||
style={{
|
||||
cursor: 'default',
|
||||
}}
|
||||
style={styles.style}
|
||||
>
|
||||
<EmojiPicker
|
||||
allowDelete={!!meta.avatar}
|
||||
@@ -118,8 +132,8 @@ const AgentHeader = memo(() => {
|
||||
<Suspense
|
||||
fallback={
|
||||
<Flexbox gap={8}>
|
||||
<Skeleton.Button block style={{ height: 38 }} />
|
||||
<Skeleton.Button block style={{ height: 38 }} />
|
||||
<Skeleton.Button block style={styles.style1} />
|
||||
<Skeleton.Button block style={styles.style1} />
|
||||
</Flexbox>
|
||||
}
|
||||
>
|
||||
@@ -154,12 +168,7 @@ const AgentHeader = memo(() => {
|
||||
debouncedSaveTitle(e.target.value);
|
||||
}}
|
||||
placeholder={t('settingAgent.name.placeholder', { ns: 'setting' })}
|
||||
style={{
|
||||
fontSize: 36,
|
||||
fontWeight: 600,
|
||||
padding: 0,
|
||||
width: '100%',
|
||||
}}
|
||||
style={styles.fullWidth}
|
||||
value={localTitle}
|
||||
variant={'borderless'}
|
||||
/>
|
||||
|
||||
+50
-36
@@ -3,8 +3,41 @@ import { Flexbox } from '@lobehub/ui';
|
||||
import { cssVar } from 'antd-style';
|
||||
import { type ReactNode, memo, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { type MentionListOption } from './types';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored1: {
|
||||
borderTop: `1px solid ${cssVar.colorBorderSecondary}`,
|
||||
},
|
||||
colored3: {
|
||||
color: cssVar.colorText,
|
||||
fontSize: 14,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
},
|
||||
coloredBase: {
|
||||
background: cssVar.colorBgElevated,
|
||||
border: `1px solid ${cssVar.colorBorderSecondary}`,
|
||||
borderRadius: 12,
|
||||
boxShadow: cssVar.boxShadowSecondary,
|
||||
maxHeight: 260,
|
||||
maxWidth: 400,
|
||||
minWidth: 150,
|
||||
overflow: 'hidden auto',
|
||||
position: 'fixed',
|
||||
zIndex: 9999,
|
||||
},
|
||||
coloredBaseActive: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
flexContainer: {
|
||||
flex: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Get cursor position from browser selection API
|
||||
*/
|
||||
@@ -47,35 +80,27 @@ const MentionDropdown = memo<MenuRenderProps>(
|
||||
|
||||
if (!open || !options.length || !position) return null;
|
||||
|
||||
const dropdownStyle = {
|
||||
...styles.coloredBase,
|
||||
left: position.x,
|
||||
top: position.y,
|
||||
};
|
||||
|
||||
return (
|
||||
<Flexbox
|
||||
style={{
|
||||
background: cssVar.colorBgElevated,
|
||||
border: `1px solid ${cssVar.colorBorderSecondary}`,
|
||||
borderRadius: 12,
|
||||
boxShadow: cssVar.boxShadowSecondary,
|
||||
left: position.x,
|
||||
maxHeight: 260,
|
||||
maxWidth: 400,
|
||||
minWidth: 150,
|
||||
overflow: 'hidden auto',
|
||||
position: 'fixed',
|
||||
top: position.y,
|
||||
zIndex: 9999,
|
||||
}}
|
||||
>
|
||||
<Flexbox style={dropdownStyle}>
|
||||
{options.map((option) => {
|
||||
if ((option as any)?.type === 'divider') {
|
||||
return (
|
||||
<div
|
||||
key={`divider-${(option as any)?.key ?? 'divider'}`}
|
||||
style={{ borderTop: `1px solid ${cssVar.colorBorderSecondary}` }}
|
||||
/>
|
||||
<div key={`divider-${(option as any)?.key ?? 'divider'}`} style={styles.colored1} />
|
||||
);
|
||||
}
|
||||
|
||||
const item = option as MentionListOption;
|
||||
const isActive = activeKey === item.key;
|
||||
const itemStyle = {
|
||||
...styles.coloredBaseActive,
|
||||
background: isActive ? cssVar.colorFillSecondary : undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
<Flexbox
|
||||
@@ -91,23 +116,12 @@ const MentionDropdown = memo<MenuRenderProps>(
|
||||
paddingBlock={8}
|
||||
paddingInline={12}
|
||||
ref={isActive ? activeItemRef : null}
|
||||
style={{
|
||||
background: isActive ? cssVar.colorFillSecondary : undefined,
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
style={itemStyle}
|
||||
>
|
||||
{item.icon && <Flexbox style={{ flex: 'none' }}>{item?.icon as ReactNode}</Flexbox>}
|
||||
<div
|
||||
style={{
|
||||
color: cssVar.colorText,
|
||||
fontSize: 14,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
{item.icon && (
|
||||
<Flexbox style={styles.flexContainer}>{item?.icon as ReactNode}</Flexbox>
|
||||
)}
|
||||
<div style={styles.colored3}>{item.label}</div>
|
||||
</Flexbox>
|
||||
);
|
||||
})}
|
||||
|
||||
+8
-1
@@ -14,10 +14,17 @@ import { useAgentStore } from '@/store/agent';
|
||||
import { pluginHelpers, useToolStore } from '@/store/tool';
|
||||
import { toolSelectors } from '@/store/tool/selectors';
|
||||
import { hydrationPrompt } from '@/utils/promptTemplate';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import MentionDropdown from './MentionDropdown';
|
||||
import { type MentionListOption, type MentionMetadata } from './types';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
flex: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
// 根据 identifier 获取 Klavis 服务器类型配置
|
||||
const getKlavisServerType = (identifier: string) =>
|
||||
KLAVIS_SERVER_TYPES.find((type) => type.identifier === identifier);
|
||||
@@ -29,7 +36,7 @@ const getKlavisServerType = (identifier: string) =>
|
||||
*/
|
||||
const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label }) => {
|
||||
if (typeof icon === 'string') {
|
||||
return <Image alt={label} height={20} src={icon} style={{ flex: 'none' }} width={20} />;
|
||||
return <Image alt={label} height={20} src={icon} style={styles.flexContainer} width={20} />;
|
||||
}
|
||||
|
||||
// 使用主题色填充,在深色模式下自动适应
|
||||
|
||||
@@ -14,6 +14,7 @@ import { useQueryRoute } from '@/hooks/useQueryRoute';
|
||||
import { useAgentStore } from '@/store/agent';
|
||||
import { agentSelectors } from '@/store/agent/selectors';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import AgentCronJobs from '../AgentCronJobs';
|
||||
import EditorCanvas from '../EditorCanvas';
|
||||
@@ -21,6 +22,19 @@ import AgentPublishButton from '../Header/AgentPublishButton';
|
||||
import AgentHeader from './AgentHeader';
|
||||
import AgentTool from './AgentTool';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
cursor: 'default',
|
||||
marginBottom: 12,
|
||||
},
|
||||
spacing1: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
spacing2: {
|
||||
marginTop: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const ProfileEditor = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
|
||||
@@ -40,18 +54,12 @@ const ProfileEditor = memo(() => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
style={{ cursor: 'default', marginBottom: 12 }}
|
||||
style={styles.spacing}
|
||||
>
|
||||
{/* Header: Avatar + Name + Description */}
|
||||
<AgentHeader />
|
||||
{/* Config Bar: Model Selector */}
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
gap={8}
|
||||
horizontal
|
||||
justify={'flex-start'}
|
||||
style={{ marginBottom: 12 }}
|
||||
>
|
||||
<Flexbox align={'center'} gap={8} horizontal justify={'flex-start'} style={styles.spacing1}>
|
||||
<ModelSelect
|
||||
onChange={updateConfig}
|
||||
value={{
|
||||
@@ -61,13 +69,7 @@ const ProfileEditor = memo(() => {
|
||||
/>
|
||||
</Flexbox>
|
||||
<AgentTool />
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
gap={8}
|
||||
horizontal
|
||||
justify={'flex-start'}
|
||||
style={{ marginTop: 16 }}
|
||||
>
|
||||
<Flexbox align={'center'} gap={8} horizontal justify={'flex-start'} style={styles.spacing2}>
|
||||
<Button
|
||||
icon={PlayIcon}
|
||||
onClick={() => {
|
||||
|
||||
+9
-1
@@ -1,8 +1,16 @@
|
||||
import { Flexbox, Tag } from '@lobehub/ui';
|
||||
import { type ReactNode, memo } from 'react';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
});
|
||||
|
||||
interface BlockProps {
|
||||
children?: ReactNode;
|
||||
count: number;
|
||||
@@ -17,7 +25,7 @@ const Block = memo<BlockProps>(({ title, count, desc, children, id }) => {
|
||||
<Title id={id} tag={<Tag>{count}</Tag>}>
|
||||
{title}
|
||||
</Title>
|
||||
<p style={{ marginBottom: 24 }}>{desc}</p>
|
||||
<p style={styles.spacing}>{desc}</p>
|
||||
{children}
|
||||
</Flexbox>
|
||||
);
|
||||
|
||||
+13
-7
@@ -8,10 +8,17 @@ import { useTranslation } from 'react-i18next';
|
||||
import { DEFAULT_USER_AVATAR_URL } from '@/const/meta';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { authSelectors, userProfileSelectors } from '@/store/user/selectors';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
const Overview = memo(() => {
|
||||
const [userAvatar, username] = useUserStore((s) => [
|
||||
userProfileSelectors.userAvatar(s),
|
||||
@@ -61,6 +68,10 @@ const Overview = memo(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const coloredStyle = {
|
||||
background: theme.colorBgContainerSecondary,
|
||||
};
|
||||
|
||||
return (
|
||||
<Flexbox gap={16}>
|
||||
<Collapse
|
||||
@@ -76,18 +87,13 @@ const Overview = memo(() => {
|
||||
variant={'outlined'}
|
||||
/>
|
||||
<Title>{t('assistants.details.overview.example')}</Title>
|
||||
<Block
|
||||
style={{
|
||||
background: theme.colorBgContainerSecondary,
|
||||
}}
|
||||
variant={'outlined'}
|
||||
>
|
||||
<Block style={coloredStyle} variant={'outlined'}>
|
||||
<ChatList
|
||||
data={data}
|
||||
renderMessages={{
|
||||
default: ({ id, editableContent }) => <div id={id}>{editableContent}</div>,
|
||||
}}
|
||||
style={{ width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
/>
|
||||
</Block>
|
||||
</Flexbox>
|
||||
|
||||
+9
-3
@@ -4,12 +4,20 @@ import { MessageCircleHeartIcon, MessageCircleQuestionIcon } from 'lucide-react'
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import TokenTag from '../../../../../(list)/agent/features/List/TokenTag';
|
||||
import Title from '../../../../../features/Title';
|
||||
import MarkdownRender from '../../../../features/MakedownRender';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import TagList from './TagList';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
|
||||
const Overview = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { tokenUsage, tags = [], config } = useDetailContext();
|
||||
@@ -36,9 +44,7 @@ const Overview = memo(() => {
|
||||
color={cssVar.colorError}
|
||||
icon={MessageCircleHeartIcon}
|
||||
size={20}
|
||||
style={{
|
||||
marginTop: 4,
|
||||
}}
|
||||
style={styles.spacing}
|
||||
/>
|
||||
<MarkdownRender>{openingMessage?.trimEnd()}</MarkdownRender>
|
||||
</Block>
|
||||
|
||||
+12
-2
@@ -10,10 +10,20 @@ import PublishedTime from '@/components/PublishedTime';
|
||||
import Link from '@/libs/router/Link';
|
||||
import { usePathname, useQuery } from '@/libs/router/navigation';
|
||||
import { type AssistantMarketSource, AssistantNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
},
|
||||
style: {
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
|
||||
const Versions = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const pathname = usePathname();
|
||||
@@ -69,7 +79,7 @@ const Versions = memo(() => {
|
||||
const statusMeta = statusKey ? statusTagMap[statusKey] : undefined;
|
||||
const content = (
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
<code style={{ fontSize: 14 }}>{record.version}</code>
|
||||
<code style={styles.style}>{record.version}</code>
|
||||
{(record.isLatest || record.version === currentVersion) && (
|
||||
<Tag color={'info'}>{t('assistants.details.version.table.isLatest')}</Tag>
|
||||
)}
|
||||
@@ -92,7 +102,7 @@ const Versions = memo(() => {
|
||||
},
|
||||
{ skipNull: true },
|
||||
)}
|
||||
style={{ color: 'inherit' }}
|
||||
style={styles.colored}
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { memo } from 'react';
|
||||
|
||||
import { useQueryState } from '@/hooks/useQueryParam';
|
||||
import { AssistantNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Sidebar from '../Sidebar';
|
||||
import Capabilities from './Capabilities';
|
||||
@@ -15,6 +16,12 @@ import Related from './Related';
|
||||
import SystemRole from './SystemRole';
|
||||
import Versions from './Versions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
const { mobile = isMobile } = useResponsive();
|
||||
const [activeTab, setActiveTab] = useQueryState('activeTab', {
|
||||
@@ -30,12 +37,7 @@ const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
horizontal={!mobile}
|
||||
style={mobile ? { flexDirection: 'column-reverse' } : undefined}
|
||||
>
|
||||
<Flexbox
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox style={styles.style} width={'100%'}>
|
||||
{activeTab === AssistantNavKey.Overview && <Overview />}
|
||||
{activeTab === AssistantNavKey.SystemRole && <SystemRole />}
|
||||
{activeTab === AssistantNavKey.Capabilities && <Capabilities />}
|
||||
|
||||
+9
-1
@@ -7,11 +7,19 @@ import urlJoin from 'url-join';
|
||||
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { type AssistantMarketSource } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import Item from './Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Related = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { related, category } = useDetailContext();
|
||||
@@ -45,7 +53,7 @@ const Related = memo(() => {
|
||||
{ skipNull: true },
|
||||
);
|
||||
return (
|
||||
<Link key={index} style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
||||
<Link key={index} style={styles.colored} to={link}>
|
||||
<Item {...item} />
|
||||
</Link>
|
||||
);
|
||||
|
||||
+10
-10
@@ -3,8 +3,17 @@ import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
margin: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const Summary = memo(() => {
|
||||
const { description, summary } = useDetailContext();
|
||||
const { t } = useTranslation('discover');
|
||||
@@ -14,16 +23,7 @@ const Summary = memo(() => {
|
||||
expandIconPlacement={'end'}
|
||||
items={[
|
||||
{
|
||||
children: (
|
||||
<p
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{summary || description}
|
||||
</p>
|
||||
),
|
||||
children: <p style={styles.spacing}>{summary || description}</p>,
|
||||
key: 'summary',
|
||||
label: t('assistants.details.summary.title'),
|
||||
},
|
||||
|
||||
@@ -3,12 +3,22 @@ import { memo } from 'react';
|
||||
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { AssistantNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import ActionButton from './ActionButton';
|
||||
import Related from './Related';
|
||||
import Summary from './Summary';
|
||||
import TocList from './TocList';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
const { activeTab = AssistantNavKey.Overview } = useQuery() as { activeTab: AssistantNavKey };
|
||||
|
||||
@@ -21,19 +31,7 @@ const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollShadow
|
||||
flex={'none'}
|
||||
gap={32}
|
||||
hideScrollBar
|
||||
size={4}
|
||||
style={{
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
}}
|
||||
width={360}
|
||||
>
|
||||
<ScrollShadow flex={'none'} gap={32} hideScrollBar size={4} style={styles.spacing} width={360}>
|
||||
<ActionButton />
|
||||
{activeTab !== AssistantNavKey.Overview && <Summary />}
|
||||
{activeTab === AssistantNavKey.SystemRole && <TocList />}
|
||||
|
||||
@@ -7,6 +7,42 @@ import { memo } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: '#8c8c8c',
|
||||
},
|
||||
colored1: {
|
||||
color: '#ff4d4f',
|
||||
},
|
||||
colored2: {
|
||||
color: '#666',
|
||||
lineHeight: 1.6,
|
||||
},
|
||||
flexContainer: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
},
|
||||
flexContainer1: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
},
|
||||
spacing: {
|
||||
margin: '16px 0',
|
||||
paddingLeft: '20px',
|
||||
textAlign: 'left',
|
||||
},
|
||||
});
|
||||
|
||||
interface StatusPageProps {
|
||||
status: 'unpublished' | 'archived' | 'deprecated';
|
||||
}
|
||||
@@ -22,16 +58,7 @@ const StatusPage = memo<StatusPageProps>(({ status }) => {
|
||||
// 审核中状态
|
||||
if (status === 'unpublished') {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<div style={styles.flexContainer}>
|
||||
<Result
|
||||
extra={
|
||||
<Button onClick={handleBackToMarket} size={'large'} type="primary">
|
||||
@@ -64,22 +91,13 @@ const StatusPage = memo<StatusPageProps>(({ status }) => {
|
||||
const isArchived = status === 'archived';
|
||||
const statusKey = isArchived ? 'archived' : 'deprecated';
|
||||
const statusIcon = isArchived ? (
|
||||
<FolderOpenOutlined style={{ color: '#8c8c8c' }} />
|
||||
<FolderOpenOutlined style={styles.colored} />
|
||||
) : (
|
||||
<ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />
|
||||
<ExclamationCircleOutlined style={styles.colored1} />
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<div style={styles.flexContainer1}>
|
||||
<Result
|
||||
extra={
|
||||
<Button onClick={handleBackToMarket} type="primary">
|
||||
@@ -88,9 +106,9 @@ const StatusPage = memo<StatusPageProps>(({ status }) => {
|
||||
}
|
||||
icon={statusIcon}
|
||||
subTitle={
|
||||
<div style={{ color: '#666', lineHeight: 1.6 }}>
|
||||
<div style={styles.colored2}>
|
||||
<p>{t(`assistants.status.${statusKey}.subtitle`)}</p>
|
||||
<ul style={{ margin: '16px 0', paddingLeft: '20px', textAlign: 'left' }}>
|
||||
<ul style={styles.spacing}>
|
||||
<li>{t(`assistants.status.${statusKey}.reasons.owner`)}</li>
|
||||
<li>{t(`assistants.status.${statusKey}.reasons.official`)}</li>
|
||||
</ul>
|
||||
|
||||
@@ -4,8 +4,23 @@ import { Flexbox, Skeleton } from '@lobehub/ui';
|
||||
import { useResponsive } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Nav from './features/Details/Nav';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
height: 36,
|
||||
width: 200,
|
||||
},
|
||||
style1: {
|
||||
width: 200,
|
||||
},
|
||||
style2: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Loading = memo(() => {
|
||||
const { mobile } = useResponsive();
|
||||
return (
|
||||
@@ -13,9 +28,9 @@ const Loading = memo(() => {
|
||||
<Flexbox gap={12}>
|
||||
<Flexbox align={'center'} gap={16} horizontal width={'100%'}>
|
||||
<Skeleton.Avatar active shape={'square'} size={mobile ? 48 : 64} />
|
||||
<Skeleton.Button active style={{ height: 36, width: 200 }} />
|
||||
<Skeleton.Button active style={styles.style} />
|
||||
</Flexbox>
|
||||
<Skeleton.Button size={'small'} style={{ width: 200 }} />
|
||||
<Skeleton.Button size={'small'} style={styles.style1} />
|
||||
</Flexbox>
|
||||
<Nav />
|
||||
<Flexbox
|
||||
@@ -23,14 +38,7 @@ const Loading = memo(() => {
|
||||
horizontal={!mobile}
|
||||
style={mobile ? { flexDirection: 'column-reverse' } : undefined}
|
||||
>
|
||||
<Flexbox
|
||||
flex={1}
|
||||
gap={16}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox flex={1} gap={16} style={styles.style2} width={'100%'}>
|
||||
<Skeleton paragraph={{ rows: 3 }} title={false} />
|
||||
<Skeleton paragraph={{ rows: 8 }} title={false} />
|
||||
<Skeleton paragraph={{ rows: 8 }} title={false} />
|
||||
|
||||
@@ -4,11 +4,19 @@ import { Flexbox } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
minHeight: 400,
|
||||
},
|
||||
});
|
||||
|
||||
const NotFound = memo(() => {
|
||||
const { t } = useTranslation('error');
|
||||
|
||||
return (
|
||||
<Flexbox align="center" height="100%" justify="center" style={{ minHeight: 400 }} width="100%">
|
||||
<Flexbox align="center" height="100%" justify="center" style={styles.style} width="100%">
|
||||
<h2>{t('notFound.title')}</h2>
|
||||
</Flexbox>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,13 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { DiscoverTab } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
},
|
||||
});
|
||||
|
||||
const Breadcrumb = memo<{ identifier: string; tab: DiscoverTab }>(({ tab, identifier }) => {
|
||||
const { t } = useTranslation('discover');
|
||||
@@ -27,14 +34,7 @@ const Breadcrumb = memo<{ identifier: string; tab: DiscoverTab }>(({ tab, identi
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<Flexbox
|
||||
align="center"
|
||||
gap={4}
|
||||
horizontal
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
}}
|
||||
>
|
||||
<Flexbox align="center" gap={4} horizontal style={styles.colored}>
|
||||
@{identifier}
|
||||
</Flexbox>
|
||||
),
|
||||
@@ -51,14 +51,7 @@ const Breadcrumb = memo<{ identifier: string; tab: DiscoverTab }>(({ tab, identi
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<Flexbox
|
||||
align="center"
|
||||
gap={4}
|
||||
horizontal
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
}}
|
||||
>
|
||||
<Flexbox align="center" gap={4} horizontal style={styles.colored}>
|
||||
{identifier}
|
||||
<CopyButton
|
||||
content={identifier}
|
||||
|
||||
@@ -5,9 +5,17 @@ import { useResponsive } from 'antd-style';
|
||||
import { type ReactNode, memo } from 'react';
|
||||
|
||||
import Footer from '@/features/Setting/Footer';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import SidebarContainer from './SidebarContainer';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
interface DetailLayoutProps {
|
||||
actions?: ReactNode;
|
||||
children?: ReactNode;
|
||||
@@ -39,7 +47,7 @@ const DetailLayout = memo<DetailLayoutProps>(
|
||||
<>
|
||||
{header}
|
||||
<Flexbox gap={32} horizontal width={'100%'}>
|
||||
<Flexbox flex={1} gap={48} style={{ overflow: 'hidden', position: 'relative' }}>
|
||||
<Flexbox flex={1} gap={48} style={styles.style}>
|
||||
{children}
|
||||
</Flexbox>
|
||||
<SidebarContainer>
|
||||
|
||||
@@ -5,8 +5,16 @@ import { FileText } from 'lucide-react';
|
||||
import { type ReactNode, memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { H1, H2, H3, H4, H5 } from './Toc/Heading';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
maxWidth: 400,
|
||||
},
|
||||
});
|
||||
|
||||
const MarkdownRender = memo<{ children?: string }>(({ children }) => {
|
||||
const { t } = useTranslation('common');
|
||||
if (!children)
|
||||
@@ -16,7 +24,7 @@ const MarkdownRender = memo<{ children?: string }>(({ children }) => {
|
||||
description={t('noContent')}
|
||||
descriptionProps={{ fontSize: 14 }}
|
||||
icon={FileText}
|
||||
style={{ maxWidth: 400 }}
|
||||
style={styles.style}
|
||||
/>
|
||||
</Center>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
import { Flexbox, type FlexboxProps } from '@lobehub/ui';
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
position: 'relative',
|
||||
width: 320,
|
||||
},
|
||||
});
|
||||
|
||||
const SidebarContainer: FC<FlexboxProps> = ({ children, style, ...rest }) => {
|
||||
return (
|
||||
<Flexbox
|
||||
flex={'none'}
|
||||
gap={48}
|
||||
height={'100%'}
|
||||
style={{ position: 'relative', width: 320, ...style }}
|
||||
style={StyleSheet.compose(styles.style, style)}
|
||||
width={'100%'}
|
||||
{...rest}
|
||||
>
|
||||
|
||||
+77
-74
@@ -4,94 +4,97 @@ import { Crown, User } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
margin: 0,
|
||||
},
|
||||
style: {
|
||||
fontSize: 12,
|
||||
},
|
||||
});
|
||||
|
||||
const { Title, Text, Paragraph } = Typography;
|
||||
|
||||
const MemberCard = memo(
|
||||
({
|
||||
agent,
|
||||
currentVersion,
|
||||
}: {
|
||||
agent: any;
|
||||
currentVersion: any;
|
||||
}) => {
|
||||
const { t } = useTranslation('discover');
|
||||
const isSupervisor = agent.role === 'supervisor';
|
||||
const MemberCard = memo(({ agent, currentVersion }: { agent: any; currentVersion: any }) => {
|
||||
const { t } = useTranslation('discover');
|
||||
const isSupervisor = agent.role === 'supervisor';
|
||||
|
||||
return (
|
||||
<Card hoverable>
|
||||
<Flexbox gap={12}>
|
||||
{/* Avatar and Basic Info */}
|
||||
<Flexbox align="center" gap={12} horizontal>
|
||||
<Avatar avatar={currentVersion.avatar || agent.name[0]} size={48} />
|
||||
<Flexbox flex={1} gap={4}>
|
||||
<Flexbox align="center" gap={8} horizontal>
|
||||
<Title level={5} style={{ margin: 0 }}>
|
||||
{currentVersion.name || agent.name}
|
||||
</Title>
|
||||
{isSupervisor ? (
|
||||
<Tag color="gold" icon={<Crown size={12} />}>
|
||||
{t('members.supervisor', { defaultValue: 'Supervisor' })}
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag color="blue" icon={<User size={12} />}>
|
||||
{t('members.participant', { defaultValue: 'Participant' })}
|
||||
</Tag>
|
||||
)}
|
||||
</Flexbox>
|
||||
<Text type="secondary">{agent.identifier}</Text>
|
||||
return (
|
||||
<Card hoverable>
|
||||
<Flexbox gap={12}>
|
||||
{/* Avatar and Basic Info */}
|
||||
<Flexbox align="center" gap={12} horizontal>
|
||||
<Avatar avatar={currentVersion.avatar || agent.name[0]} size={48} />
|
||||
<Flexbox flex={1} gap={4}>
|
||||
<Flexbox align="center" gap={8} horizontal>
|
||||
<Title level={5} style={styles.spacing}>
|
||||
{currentVersion.name || agent.name}
|
||||
</Title>
|
||||
{isSupervisor ? (
|
||||
<Tag color="gold" icon={<Crown size={12} />}>
|
||||
{t('members.supervisor', { defaultValue: 'Supervisor' })}
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag color="blue" icon={<User size={12} />}>
|
||||
{t('members.participant', { defaultValue: 'Participant' })}
|
||||
</Tag>
|
||||
)}
|
||||
</Flexbox>
|
||||
<Text type="secondary">{agent.identifier}</Text>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
|
||||
{/* Description */}
|
||||
{currentVersion.description && (
|
||||
<Paragraph ellipsis={{ rows: 2 }} style={{ margin: 0 }} type="secondary">
|
||||
{currentVersion.description}
|
||||
{/* Description */}
|
||||
{currentVersion.description && (
|
||||
<Paragraph ellipsis={{ rows: 2 }} style={styles.spacing} type="secondary">
|
||||
{currentVersion.description}
|
||||
</Paragraph>
|
||||
)}
|
||||
|
||||
{/* System Role (if available) */}
|
||||
{currentVersion.config?.systemRole && (
|
||||
<Flexbox gap={4}>
|
||||
<Text strong>{t('members.systemRole', { defaultValue: 'System Role' })}:</Text>
|
||||
<Paragraph ellipsis={{ rows: 3 }} style={styles.spacing} type="secondary">
|
||||
{currentVersion.config.systemRole}
|
||||
</Paragraph>
|
||||
)}
|
||||
|
||||
{/* System Role (if available) */}
|
||||
{currentVersion.config?.systemRole && (
|
||||
<Flexbox gap={4}>
|
||||
<Text strong>{t('members.systemRole', { defaultValue: 'System Role' })}:</Text>
|
||||
<Paragraph ellipsis={{ rows: 3 }} style={{ margin: 0 }} type="secondary">
|
||||
{currentVersion.config.systemRole}
|
||||
</Paragraph>
|
||||
</Flexbox>
|
||||
)}
|
||||
|
||||
{/* Metadata */}
|
||||
<Flexbox gap={8} horizontal wrap="wrap">
|
||||
{currentVersion.version && (
|
||||
<Text type="secondary">
|
||||
{t('members.version', { defaultValue: 'Version' })}: {currentVersion.version}
|
||||
</Text>
|
||||
)}
|
||||
{currentVersion.tokenUsage !== undefined && (
|
||||
<Text type="secondary">
|
||||
{t('members.tokenUsage', { defaultValue: 'Token Usage' })}:{' '}
|
||||
{currentVersion.tokenUsage}
|
||||
</Text>
|
||||
)}
|
||||
</Flexbox>
|
||||
)}
|
||||
|
||||
{/* URL */}
|
||||
{currentVersion.url && (
|
||||
<Text
|
||||
copyable={{ text: currentVersion.url }}
|
||||
ellipsis
|
||||
style={{ fontSize: 12 }}
|
||||
type="secondary"
|
||||
>
|
||||
{currentVersion.url}
|
||||
{/* Metadata */}
|
||||
<Flexbox gap={8} horizontal wrap="wrap">
|
||||
{currentVersion.version && (
|
||||
<Text type="secondary">
|
||||
{t('members.version', { defaultValue: 'Version' })}: {currentVersion.version}
|
||||
</Text>
|
||||
)}
|
||||
{currentVersion.tokenUsage !== undefined && (
|
||||
<Text type="secondary">
|
||||
{t('members.tokenUsage', { defaultValue: 'Token Usage' })}:{' '}
|
||||
{currentVersion.tokenUsage}
|
||||
</Text>
|
||||
)}
|
||||
</Flexbox>
|
||||
</Card>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
{/* URL */}
|
||||
{currentVersion.url && (
|
||||
<Text
|
||||
copyable={{ text: currentVersion.url }}
|
||||
ellipsis
|
||||
style={styles.style}
|
||||
type="secondary"
|
||||
>
|
||||
{currentVersion.url}
|
||||
</Text>
|
||||
)}
|
||||
</Flexbox>
|
||||
</Card>
|
||||
);
|
||||
});
|
||||
|
||||
MemberCard.displayName = 'MemberCard';
|
||||
|
||||
|
||||
+24
-14
@@ -6,10 +6,30 @@ import { useNavigate } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { type DiscoverGroupAgentItem } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
border: '1px solid var(--lobe-border-color)',
|
||||
borderRadius: 8,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s',
|
||||
},
|
||||
colored1: {
|
||||
color: '#999',
|
||||
},
|
||||
style: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
style1: {
|
||||
fontSize: 12,
|
||||
opacity: 0.65,
|
||||
},
|
||||
});
|
||||
|
||||
const GroupAgentCard = memo<DiscoverGroupAgentItem>((item) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -23,24 +43,14 @@ const GroupAgentCard = memo<DiscoverGroupAgentItem>((item) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Flexbox
|
||||
gap={12}
|
||||
onClick={handleClick}
|
||||
padding={16}
|
||||
style={{
|
||||
border: '1px solid var(--lobe-border-color)',
|
||||
borderRadius: 8,
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s',
|
||||
}}
|
||||
>
|
||||
<Flexbox gap={12} onClick={handleClick} padding={16} style={styles.colored}>
|
||||
<Flexbox align="center" gap={12} horizontal>
|
||||
<Avatar avatar={item.avatar || item.title[0]} shape="square" size={48} />
|
||||
<Flexbox flex={1} gap={4}>
|
||||
<Text ellipsis style={{ fontWeight: 500 }}>
|
||||
<Text ellipsis style={styles.style}>
|
||||
{item.title}
|
||||
</Text>
|
||||
<Text ellipsis style={{ fontSize: 12, opacity: 0.65 }} type="secondary">
|
||||
<Text ellipsis style={styles.style1} type="secondary">
|
||||
{item.description}
|
||||
</Text>
|
||||
</Flexbox>
|
||||
@@ -76,7 +86,7 @@ const Related = memo(() => {
|
||||
))}
|
||||
</Grid>
|
||||
) : (
|
||||
<Flexbox align="center" padding={32} style={{ color: '#999' }}>
|
||||
<Flexbox align="center" padding={32} style={styles.colored1}>
|
||||
{t('groupAgents.details.related.empty', {
|
||||
defaultValue: 'No related group agents found',
|
||||
})}
|
||||
|
||||
+18
-4
@@ -4,11 +4,19 @@ import { MessageCircleHeartIcon, MessageCircleQuestionIcon } from 'lucide-react'
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import MarkdownRender from '../../../../features/MakedownRender';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import TagList from './TagList';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
marginTop: 4,
|
||||
},
|
||||
});
|
||||
|
||||
const SystemRole = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { tokenUsage, tags = [], config } = useDetailContext();
|
||||
@@ -18,7 +26,15 @@ const SystemRole = memo(() => {
|
||||
<Flexbox gap={16}>
|
||||
{systemRole && (
|
||||
<>
|
||||
<Title tag={tokenUsage && <Tag>{t('groupAgents.details.tokenUsage', { defaultValue: `${tokenUsage} tokens` })}</Tag>}>
|
||||
<Title
|
||||
tag={
|
||||
tokenUsage && (
|
||||
<Tag>
|
||||
{t('groupAgents.details.tokenUsage', { defaultValue: `${tokenUsage} tokens` })}
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
>
|
||||
{t('groupAgents.details.systemRole.title', { defaultValue: 'System Role' })}
|
||||
</Title>
|
||||
<Block gap={16} padding={16} variant={'outlined'}>
|
||||
@@ -39,9 +55,7 @@ const SystemRole = memo(() => {
|
||||
color={cssVar.colorError}
|
||||
icon={MessageCircleHeartIcon}
|
||||
size={20}
|
||||
style={{
|
||||
marginTop: 4,
|
||||
}}
|
||||
style={styles.spacing}
|
||||
/>
|
||||
<MarkdownRender>{openingMessage?.trimEnd()}</MarkdownRender>
|
||||
</Block>
|
||||
|
||||
+14
-10
@@ -6,10 +6,17 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import InlineTable from '@/components/InlineTable';
|
||||
import PublishedTime from '@/components/PublishedTime';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
|
||||
const Versions = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { versions = [], currentVersion } = useDetailContext();
|
||||
@@ -39,9 +46,7 @@ const Versions = memo(() => {
|
||||
if (!versions.length) {
|
||||
return (
|
||||
<Flexbox gap={16}>
|
||||
<Title>
|
||||
{t('groupAgents.details.version.title', { defaultValue: 'Version History' })}
|
||||
</Title>
|
||||
<Title>{t('groupAgents.details.version.title', { defaultValue: 'Version History' })}</Title>
|
||||
<Block padding={24} variant={'outlined'}>
|
||||
{t('groupAgents.details.version.empty', { defaultValue: 'No version history available' })}
|
||||
</Block>
|
||||
@@ -51,9 +56,7 @@ const Versions = memo(() => {
|
||||
|
||||
return (
|
||||
<Flexbox gap={16}>
|
||||
<Title>
|
||||
{t('groupAgents.details.version.title', { defaultValue: 'Version History' })}
|
||||
</Title>
|
||||
<Title>{t('groupAgents.details.version.title', { defaultValue: 'Version History' })}</Title>
|
||||
<Block variant={'outlined'}>
|
||||
<InlineTable
|
||||
columns={[
|
||||
@@ -61,18 +64,19 @@ const Versions = memo(() => {
|
||||
dataIndex: 'version',
|
||||
render: (_: any, record: any) => {
|
||||
const statusKey =
|
||||
record.status &&
|
||||
Object.prototype.hasOwnProperty.call(statusTagMap, record.status)
|
||||
record.status && Object.prototype.hasOwnProperty.call(statusTagMap, record.status)
|
||||
? (record.status as keyof typeof statusTagMap)
|
||||
: undefined;
|
||||
const statusMeta = statusKey ? statusTagMap[statusKey] : undefined;
|
||||
|
||||
return (
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
<code style={{ fontSize: 14 }}>{record.version}</code>
|
||||
<code style={styles.style}>{record.version}</code>
|
||||
{(record.isLatest || record.version === currentVersion) && (
|
||||
<Tag color={'info'}>
|
||||
{t('groupAgents.details.version.table.isLatest', { defaultValue: 'Latest' })}
|
||||
{t('groupAgents.details.version.table.isLatest', {
|
||||
defaultValue: 'Latest',
|
||||
})}
|
||||
</Tag>
|
||||
)}
|
||||
{statusMeta && <Tag color={statusMeta.color}>{statusMeta.label}</Tag>}
|
||||
|
||||
+10
-6
@@ -3,6 +3,7 @@ import { useResponsive } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { useQueryState } from '@/hooks/useQueryParam';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Sidebar from '../Sidebar';
|
||||
import Nav, { GroupAgentNavKey } from './Nav';
|
||||
@@ -10,6 +11,12 @@ import Overview from './Overview';
|
||||
import SystemRole from './SystemRole';
|
||||
import Versions from './Versions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
const { mobile = isMobile } = useResponsive();
|
||||
const [activeTab, setActiveTab] = useQueryState('activeTab', {
|
||||
@@ -29,12 +36,9 @@ const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
horizontal={!mobile}
|
||||
style={mobile ? { flexDirection: 'column-reverse' } : undefined}
|
||||
>
|
||||
<Flexbox
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
{/* Main Content */}
|
||||
<Flexbox style={styles.style} width={'100%'}>
|
||||
{/* Tab Content */}
|
||||
{activeTab === GroupAgentNavKey.Overview && <Overview />}
|
||||
{activeTab === GroupAgentNavKey.SystemRole && <SystemRole />}
|
||||
{activeTab === GroupAgentNavKey.Versions && <Versions />}
|
||||
|
||||
+10
-10
@@ -3,8 +3,17 @@ import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
margin: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const Summary = memo(() => {
|
||||
const { description, summary } = useDetailContext();
|
||||
const { t } = useTranslation('discover');
|
||||
@@ -17,16 +26,7 @@ const Summary = memo(() => {
|
||||
expandIconPlacement={'end'}
|
||||
items={[
|
||||
{
|
||||
children: (
|
||||
<p
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{displayDescription}
|
||||
</p>
|
||||
),
|
||||
children: <p style={styles.spacing}>{displayDescription}</p>,
|
||||
key: 'summary',
|
||||
label: t('groupAgents.details.summary.title', {
|
||||
defaultValue: 'What can you use this group for?',
|
||||
|
||||
+11
-13
@@ -2,11 +2,21 @@ import { Flexbox, ScrollShadow } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { GroupAgentNavKey } from '../Details/Nav';
|
||||
import ActionButton from './ActionButton';
|
||||
import Summary from './Summary';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
const { activeTab = GroupAgentNavKey.Overview } = useQuery() as { activeTab: GroupAgentNavKey };
|
||||
|
||||
@@ -19,19 +29,7 @@ const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollShadow
|
||||
flex={'none'}
|
||||
gap={32}
|
||||
hideScrollBar
|
||||
size={4}
|
||||
style={{
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
}}
|
||||
width={360}
|
||||
>
|
||||
<ScrollShadow flex={'none'} gap={32} hideScrollBar size={4} style={styles.spacing} width={360}>
|
||||
<ActionButton />
|
||||
{activeTab !== GroupAgentNavKey.Overview && <Summary />}
|
||||
</ScrollShadow>
|
||||
|
||||
+36
-23
@@ -7,6 +7,37 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: '#8c8c8c',
|
||||
},
|
||||
colored1: {
|
||||
color: '#ff4d4f',
|
||||
},
|
||||
colored2: {
|
||||
color: '#666',
|
||||
lineHeight: 1.6,
|
||||
},
|
||||
flexContainer: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
},
|
||||
flexContainer1: {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
},
|
||||
});
|
||||
|
||||
interface StatusPageProps {
|
||||
status: 'unpublished' | 'archived' | 'deprecated';
|
||||
}
|
||||
@@ -22,16 +53,7 @@ const StatusPage = memo<StatusPageProps>(({ status }) => {
|
||||
// Unpublished status
|
||||
if (status === 'unpublished') {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<div style={styles.flexContainer}>
|
||||
<Result
|
||||
extra={
|
||||
<Button onClick={handleBackToMarket} size={'large'} type="primary">
|
||||
@@ -61,22 +83,13 @@ const StatusPage = memo<StatusPageProps>(({ status }) => {
|
||||
const isArchived = status === 'archived';
|
||||
const statusKey = isArchived ? 'archived' : 'deprecated';
|
||||
const statusIcon = isArchived ? (
|
||||
<FolderOpenOutlined style={{ color: '#8c8c8c' }} />
|
||||
<FolderOpenOutlined style={styles.colored} />
|
||||
) : (
|
||||
<ExclamationCircleOutlined style={{ color: '#ff4d4f' }} />
|
||||
<ExclamationCircleOutlined style={styles.colored1} />
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
minHeight: '60vh',
|
||||
padding: '20px',
|
||||
}}
|
||||
>
|
||||
<div style={styles.flexContainer1}>
|
||||
<Result
|
||||
extra={
|
||||
<Button onClick={handleBackToMarket} type="primary">
|
||||
@@ -85,7 +98,7 @@ const StatusPage = memo<StatusPageProps>(({ status }) => {
|
||||
}
|
||||
icon={statusIcon}
|
||||
subTitle={
|
||||
<div style={{ color: '#666', lineHeight: 1.6 }}>
|
||||
<div style={styles.colored2}>
|
||||
<p>
|
||||
{t(`groupAgents.status.${statusKey}.subtitle`, {
|
||||
defaultValue: `This group agent has been ${statusKey}.`,
|
||||
|
||||
+13
-3
@@ -5,14 +5,24 @@ import qs from 'query-string';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import InlineTable from '@/components/InlineTable';
|
||||
import Link from '@/libs/router/Link';
|
||||
import { usePathname } from '@/libs/router/navigation';
|
||||
import InlineTable from '@/components/InlineTable';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import PublishedTime from '../../../../../../../../../components/PublishedTime';
|
||||
import { useDetailContext } from '../../../../../../../../../features/MCPPluginDetail/DetailProvider';
|
||||
import Title from '../../../../../features/Title';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
},
|
||||
style: {
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
|
||||
const Versions = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { versions } = useDetailContext();
|
||||
@@ -33,10 +43,10 @@ const Versions = memo(() => {
|
||||
},
|
||||
url: pathname,
|
||||
})}
|
||||
style={{ color: 'inherit' }}
|
||||
style={styles.colored}
|
||||
>
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
<code style={{ fontSize: 14 }}>{record.version}</code>
|
||||
<code style={styles.style}>{record.version}</code>
|
||||
{record.isLatest && (
|
||||
<Tag color={'info'}>{t('mcp.details.versions.table.isLatest')}</Tag>
|
||||
)}
|
||||
|
||||
@@ -11,11 +11,18 @@ import Schema from '@/features/MCPPluginDetail/Schema';
|
||||
import Score from '@/features/MCPPluginDetail/Score';
|
||||
import { useQueryState } from '@/hooks/useQueryParam';
|
||||
import { McpNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Sidebar from '../Sidebar';
|
||||
import Related from './Related';
|
||||
import Versions from './Versions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
const { mobile = isMobile } = useResponsive();
|
||||
const [activeTab, setActiveTab] = useQueryState('activeTab', {
|
||||
@@ -36,13 +43,7 @@ const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
horizontal={!mobile}
|
||||
style={mobile ? { flexDirection: 'column-reverse' } : undefined}
|
||||
>
|
||||
<Flexbox
|
||||
flex={1}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox flex={1} style={styles.style} width={'100%'}>
|
||||
{activeTab === McpNavKey.Overview && <Overview />}
|
||||
{activeTab === McpNavKey.Deployment && <Deployment mobile={mobile} />}
|
||||
{activeTab === McpNavKey.Schema && <Schema />}
|
||||
|
||||
@@ -6,10 +6,18 @@ import { Link } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { useDetailContext } from '@/features/MCPPluginDetail/DetailProvider';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import Item from './Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Related = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { related, category } = useDetailContext();
|
||||
@@ -31,7 +39,7 @@ const Related = memo(() => {
|
||||
{related?.map((item, index) => {
|
||||
const link = urlJoin('/community/mcp', item.identifier);
|
||||
return (
|
||||
<Link key={index} style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
||||
<Link key={index} style={styles.colored} to={link}>
|
||||
<Item {...item} />
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { memo } from 'react';
|
||||
import { isDesktop } from '@/const/version';
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { McpNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import ActionButton from './ActionButton';
|
||||
import ConnectionTypeAlert from './ConnectionTypeAlert';
|
||||
@@ -11,6 +12,15 @@ import Related from './Related';
|
||||
import ServerConfig from './ServerConfig';
|
||||
import TocList from './TocList';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
const { activeTab = McpNavKey.Overview } = useQuery() as { activeTab: McpNavKey };
|
||||
|
||||
@@ -25,19 +35,7 @@ const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollShadow
|
||||
flex={'none'}
|
||||
gap={32}
|
||||
hideScrollBar
|
||||
size={4}
|
||||
style={{
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
}}
|
||||
width={360}
|
||||
>
|
||||
<ScrollShadow flex={'none'} gap={32} hideScrollBar size={4} style={styles.spacing} width={360}>
|
||||
{isDesktop ? (
|
||||
<Flexbox>
|
||||
<ActionButton />
|
||||
|
||||
+13
-6
@@ -14,9 +14,19 @@ import { ModelInfoTags } from '@/components/ModelSelect';
|
||||
import { BASE_PROVIDER_DOC_URL } from '@/const/url';
|
||||
import { formatPriceByCurrency, formatTokenNumber } from '@/utils/format';
|
||||
import { getTextInputUnitRate, getTextOutputUnitRate } from '@/utils/pricing';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useDetailContext } from '../../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
},
|
||||
style: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
});
|
||||
|
||||
const ProviderList = memo(() => {
|
||||
const { providers = [] } = useDetailContext();
|
||||
const { t } = useTranslation('discover');
|
||||
@@ -31,10 +41,10 @@ const ProviderList = memo(() => {
|
||||
key: 'provider',
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Link style={{ color: 'inherit' }} to={urlJoin('/community/provider', record.id)}>
|
||||
<Link style={styles.colored} to={urlJoin('/community/provider', record.id)}>
|
||||
<Flexbox align="center" gap={8} horizontal>
|
||||
<ProviderIcon provider={record.id} size={24} type={'avatar'} />
|
||||
<div style={{ fontWeight: 500 }}>{record.name}</div>
|
||||
<div style={styles.style}>{record.name}</div>
|
||||
</Flexbox>
|
||||
</Link>
|
||||
);
|
||||
@@ -167,10 +177,7 @@ const ProviderList = memo(() => {
|
||||
<ActionIcon icon={BookIcon} size={'small'} variant={'filled'} />
|
||||
</a>
|
||||
</Tooltip>
|
||||
<Link
|
||||
style={{ color: 'inherit' }}
|
||||
to={urlJoin('/community/provider', record.id)}
|
||||
>
|
||||
<Link style={styles.colored} to={urlJoin('/community/provider', record.id)}>
|
||||
<ActionIcon
|
||||
color={cssVar.colorTextDescription}
|
||||
icon={ChevronRightIcon}
|
||||
|
||||
+18
-3
@@ -4,8 +4,23 @@ import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Statistic from '../../../../../components/Statistic';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
margin: 0,
|
||||
},
|
||||
spacing1: {
|
||||
margin: 0,
|
||||
},
|
||||
spacing2: {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
});
|
||||
|
||||
const DEFAULT_DOC_URL = 'https://lobehub.com/docs/usage/agents/model';
|
||||
|
||||
export interface ParameterItemProps {
|
||||
@@ -26,14 +41,14 @@ const ParameterItem = memo<ParameterItemProps>(
|
||||
|
||||
return (
|
||||
<Flexbox align={'flex-start'} gap={16}>
|
||||
<p style={{ color: cssVar.colorTextSecondary, margin: 0 }}>
|
||||
<p style={styles.spacing}>
|
||||
{desc}{' '}
|
||||
<a href={docUrl} rel="noreferrer" target="_blank">
|
||||
{t('models.parameterList.docs')}
|
||||
</a>
|
||||
</p>
|
||||
<Divider dashed style={{ margin: 0 }} />
|
||||
<Flexbox align={'center'} gap={16} horizontal style={{ paddingBottom: 8 }} wrap={'wrap'}>
|
||||
<Divider dashed style={styles.spacing1} />
|
||||
<Flexbox align={'center'} gap={16} horizontal style={styles.spacing2} wrap={'wrap'}>
|
||||
<Statistic
|
||||
gap={4}
|
||||
title={t('models.parameterList.type')}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { memo } from 'react';
|
||||
|
||||
import { useQueryState } from '@/hooks/useQueryParam';
|
||||
import { ModelNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Sidebar from '../Sidebar';
|
||||
import Nav from './Nav';
|
||||
@@ -13,6 +14,12 @@ import Overview from './Overview';
|
||||
import Parameter from './Parameter';
|
||||
import Related from './Related';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
const { mobile = isMobile } = useResponsive();
|
||||
const [activeTab, setActiveTab] = useQueryState('activeTab', {
|
||||
@@ -28,12 +35,7 @@ const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
horizontal={!mobile}
|
||||
style={mobile ? { flexDirection: 'column-reverse' } : undefined}
|
||||
>
|
||||
<Flexbox
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox style={styles.style} width={'100%'}>
|
||||
{activeTab === ModelNavKey.Overview && <Overview />}
|
||||
{activeTab === ModelNavKey.Parameter && <Parameter />}
|
||||
{activeTab === ModelNavKey.Related && <Related />}
|
||||
|
||||
+10
-1
@@ -5,10 +5,19 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import Item from './Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Related = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { related, category } = useDetailContext();
|
||||
@@ -30,7 +39,7 @@ const Related = memo(() => {
|
||||
{related?.map((item, index) => {
|
||||
const link = urlJoin('/community/model', item.identifier);
|
||||
return (
|
||||
<Link key={index} style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
||||
<Link key={index} style={styles.colored} to={link}>
|
||||
<Item {...item} />
|
||||
</Link>
|
||||
);
|
||||
|
||||
+10
-1
@@ -4,10 +4,19 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import Item from './Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Related = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { providers = [] } = useDetailContext();
|
||||
@@ -21,7 +30,7 @@ const Related = memo(() => {
|
||||
{providers.slice(0, 6).map((item, index) => {
|
||||
const link = urlJoin('/community/provider', item.id);
|
||||
return (
|
||||
<Link key={index} style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
||||
<Link key={index} style={styles.colored} to={link}>
|
||||
<Item {...item} />
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -3,11 +3,21 @@ import { memo } from 'react';
|
||||
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { ModelNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import ActionButton from './ActionButton';
|
||||
import Related from './Related';
|
||||
import RelatedProviders from './RelatedProviders';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
const { activeTab = ModelNavKey.Overview } = useQuery() as { activeTab: ModelNavKey };
|
||||
|
||||
@@ -20,19 +30,7 @@ const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollShadow
|
||||
flex={'none'}
|
||||
gap={32}
|
||||
hideScrollBar
|
||||
size={4}
|
||||
style={{
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
}}
|
||||
width={360}
|
||||
>
|
||||
<ScrollShadow flex={'none'} gap={32} hideScrollBar size={4} style={styles.spacing} width={360}>
|
||||
<ActionButton />
|
||||
{activeTab !== ModelNavKey.Related && <Related />}
|
||||
{activeTab !== ModelNavKey.Overview && <RelatedProviders />}
|
||||
|
||||
+9
-1
@@ -4,8 +4,16 @@ import { BookOpen } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
maxWidth: 400,
|
||||
},
|
||||
});
|
||||
|
||||
const Guide = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { readme = '' } = useDetailContext();
|
||||
@@ -17,7 +25,7 @@ const Guide = memo(() => {
|
||||
description={t('providers.details.guide.title')}
|
||||
descriptionProps={{ fontSize: 14 }}
|
||||
icon={BookOpen}
|
||||
style={{ maxWidth: 400 }}
|
||||
style={styles.style}
|
||||
/>
|
||||
</Block>
|
||||
);
|
||||
|
||||
+22
-7
@@ -13,9 +13,26 @@ import InlineTable from '@/components/InlineTable';
|
||||
import { ModelInfoTags } from '@/components/ModelSelect';
|
||||
import { formatPriceByCurrency, formatTokenNumber } from '@/utils/format';
|
||||
import { getTextInputUnitRate, getTextOutputUnitRate } from '@/utils/pricing';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useDetailContext } from '../../../DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
},
|
||||
colored1: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
fontSize: 12,
|
||||
},
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
style1: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
});
|
||||
|
||||
const ModelList = memo(() => {
|
||||
const { models = [] } = useDetailContext();
|
||||
const { t } = useTranslation('discover');
|
||||
@@ -30,14 +47,12 @@ const ModelList = memo(() => {
|
||||
key: 'model',
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Link style={{ color: 'inherit' }} to={urlJoin('/community/model', record.id)}>
|
||||
<Link style={styles.colored} to={urlJoin('/community/model', record.id)}>
|
||||
<Flexbox align="center" gap={8} horizontal>
|
||||
<ModelIcon model={record.id} size={24} type={'avatar'} />
|
||||
<Flexbox style={{ overflow: 'hidden' }}>
|
||||
<div style={{ fontWeight: 500 }}>{record.displayName}</div>
|
||||
<div style={{ color: cssVar.colorTextSecondary, fontSize: 12 }}>
|
||||
{record.id}
|
||||
</div>
|
||||
<Flexbox style={styles.style}>
|
||||
<div style={styles.style1}>{record.displayName}</div>
|
||||
<div style={styles.colored1}>{record.id}</div>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
</Link>
|
||||
@@ -132,7 +147,7 @@ const ModelList = memo(() => {
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Flexbox align="center" gap={4} horizontal justify={'flex-end'}>
|
||||
<Link style={{ color: 'inherit' }} to={urlJoin('/community/model', record.id)}>
|
||||
<Link style={styles.colored} to={urlJoin('/community/model', record.id)}>
|
||||
<ActionIcon
|
||||
color={cssVar.colorTextDescription}
|
||||
icon={ChevronRightIcon}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { memo } from 'react';
|
||||
|
||||
import { useQueryState } from '@/hooks/useQueryParam';
|
||||
import { ProviderNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Sidebar from '../Sidebar';
|
||||
import Guide from './Guide';
|
||||
@@ -13,6 +14,12 @@ import Nav from './Nav';
|
||||
import Overview from './Overview';
|
||||
import Related from './Related';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
const { mobile = isMobile } = useResponsive();
|
||||
const [activeTab, setActiveTab] = useQueryState('activeTab', {
|
||||
@@ -28,12 +35,7 @@ const Details = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
horizontal={!mobile}
|
||||
style={mobile ? { flexDirection: 'column-reverse' } : undefined}
|
||||
>
|
||||
<Flexbox
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
width={'100%'}
|
||||
>
|
||||
<Flexbox style={styles.style} width={'100%'}>
|
||||
{activeTab === ProviderNavKey.Overview && <Overview />}
|
||||
{activeTab === ProviderNavKey.Guide && <Guide />}
|
||||
{activeTab === ProviderNavKey.Related && <Related />}
|
||||
|
||||
@@ -8,8 +8,20 @@ import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useDetailContext } from './DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
},
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
},
|
||||
});
|
||||
|
||||
const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
const { t } = useTranslation('providers');
|
||||
const { identifier, url, modelsUrl, name } = useDetailContext();
|
||||
@@ -22,10 +34,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
gap={8}
|
||||
horizontal
|
||||
justify={'space-between'}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
}}
|
||||
style={styles.style}
|
||||
>
|
||||
<Flexbox align={'flex-start'} width={'100%'}>
|
||||
<ProviderCombine provider={identifier} size={mobile ? 32 : 48} />
|
||||
@@ -65,14 +74,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
gap={mobile ? 12 : 24}
|
||||
horizontal
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
}}
|
||||
>
|
||||
<Flexbox align={'center'} gap={mobile ? 12 : 24} horizontal style={styles.colored}>
|
||||
{t(`${identifier}.description`)}
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
|
||||
+8
-1
@@ -7,11 +7,18 @@ import { useTranslation } from 'react-i18next';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { OFFICIAL_URL } from '@/const/url';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import ShareButton from '../../../../features/ShareButton';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import ProviderConfig from './ProviderConfig';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
margin: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const ActionButton = memo(() => {
|
||||
const { models = [], identifier, name } = useDetailContext();
|
||||
const { t } = useTranslation('providers');
|
||||
@@ -28,7 +35,7 @@ const ActionButton = memo(() => {
|
||||
.slice(0, 4)
|
||||
.filter(Boolean)
|
||||
.map((item) => (
|
||||
<ModelTag key={item.id} model={item.id} style={{ margin: 0 }} />
|
||||
<ModelTag key={item.id} model={item.id} style={styles.spacing} />
|
||||
))}
|
||||
{models.length > 3 && <Tag>+{models.length - 3}</Tag>}
|
||||
</Flexbox>
|
||||
|
||||
+10
-1
@@ -4,10 +4,19 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import Item from './Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Related = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { related } = useDetailContext();
|
||||
@@ -21,7 +30,7 @@ const Related = memo(() => {
|
||||
{related?.map((item, index) => {
|
||||
const link = urlJoin('/community/provider', item.identifier);
|
||||
return (
|
||||
<Link key={index} style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
||||
<Link key={index} style={styles.colored} to={link}>
|
||||
<Item {...item} />
|
||||
</Link>
|
||||
);
|
||||
|
||||
+10
-1
@@ -5,10 +5,19 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import urlJoin from 'url-join';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Title from '../../../../../features/Title';
|
||||
import { useDetailContext } from '../../DetailProvider';
|
||||
import Item from './Item';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: 'inherit',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const Related = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { models = [], identifier } = useDetailContext();
|
||||
@@ -30,7 +39,7 @@ const Related = memo(() => {
|
||||
{models?.slice(0, 6)?.map((item, index) => {
|
||||
const link = urlJoin('/community/model', item.id);
|
||||
return (
|
||||
<Link key={index} style={{ color: 'inherit', overflow: 'hidden' }} to={link}>
|
||||
<Link key={index} style={styles.colored} to={link}>
|
||||
<Item {...item} />
|
||||
</Link>
|
||||
);
|
||||
|
||||
@@ -3,11 +3,21 @@ import { memo } from 'react';
|
||||
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { ProviderNavKey } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import ActionButton from './ActionButton';
|
||||
import Related from './Related';
|
||||
import RelatedModels from './RelatedModels';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
},
|
||||
});
|
||||
|
||||
const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
const { activeTab = ProviderNavKey.Overview } = useQuery() as { activeTab: ProviderNavKey };
|
||||
|
||||
@@ -20,19 +30,7 @@ const Sidebar = memo<{ mobile?: boolean }>(({ mobile }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollShadow
|
||||
flex={'none'}
|
||||
gap={32}
|
||||
hideScrollBar
|
||||
size={4}
|
||||
style={{
|
||||
maxHeight: 'calc(100vh - 76px)',
|
||||
paddingBottom: 24,
|
||||
position: 'sticky',
|
||||
top: 16,
|
||||
}}
|
||||
width={360}
|
||||
>
|
||||
<ScrollShadow flex={'none'} gap={32} hideScrollBar size={4} style={styles.spacing} width={360}>
|
||||
<ActionButton />
|
||||
{activeTab !== ProviderNavKey.Related && <Related />}
|
||||
{activeTab !== ProviderNavKey.Overview && <RelatedModels />}
|
||||
|
||||
@@ -7,6 +7,14 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
|
||||
import { socialService } from '@/services/social';
|
||||
import { useDiscoverStore } from '@/store/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
fontWeight: 500,
|
||||
minWidth: 120,
|
||||
},
|
||||
});
|
||||
|
||||
interface FollowButtonProps {
|
||||
userId: number;
|
||||
@@ -56,10 +64,7 @@ const FollowButton = memo<FollowButtonProps>(({ userId }) => {
|
||||
onClick={handleClick}
|
||||
shape={'round'}
|
||||
size={'large'}
|
||||
style={{
|
||||
fontWeight: 500,
|
||||
minWidth: 120,
|
||||
}}
|
||||
style={styles.style}
|
||||
type={isFollowing ? 'default' : 'primary'}
|
||||
>
|
||||
{isFollowing ? t('user.unfollow') : t('user.follow')}
|
||||
|
||||
@@ -4,8 +4,16 @@ import { Flexbox, Text } from '@lobehub/ui';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useUserDetailContext } from './DetailProvider';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
fontWeight: 600,
|
||||
},
|
||||
});
|
||||
|
||||
const FollowStats = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { user } = useUserDetailContext();
|
||||
@@ -18,11 +26,11 @@ const FollowStats = memo(() => {
|
||||
return (
|
||||
<Flexbox align={'center'} gap={16} horizontal>
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
<Text style={{ fontWeight: 600 }}>{followingCount}</Text>
|
||||
<Text style={styles.style}>{followingCount}</Text>
|
||||
<Text type={'secondary'}>{t('user.following')}</Text>
|
||||
</Flexbox>
|
||||
<Flexbox align={'center'} gap={8} horizontal>
|
||||
<Text style={{ fontWeight: 600 }}>{followersCount}</Text>
|
||||
<Text style={styles.style}>{followersCount}</Text>
|
||||
<Text type={'secondary'}>{t('user.followers')}</Text>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
|
||||
@@ -7,11 +7,26 @@ import { Globe } from 'lucide-react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import { useUserDetailContext } from '../DetailProvider';
|
||||
import FollowButton from '../FollowButton';
|
||||
import FollowStats from '../FollowStats';
|
||||
import Banner from './Banner';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
boxShadow: `0 0 0 4px ${cssVar.colorBgContainer}`,
|
||||
flexShrink: 0,
|
||||
},
|
||||
spacing: {
|
||||
margin: 0,
|
||||
},
|
||||
style: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
const UserHeader = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
const { user, isOwner, onEditProfile } = useUserDetailContext();
|
||||
@@ -42,20 +57,10 @@ const UserHeader = memo(() => {
|
||||
<>
|
||||
<Banner avatar={avatarUrl} bannerUrl={bannerUrl} />
|
||||
<Flexbox gap={16}>
|
||||
<Avatar
|
||||
avatar={avatarUrl}
|
||||
shape={'square'}
|
||||
size={64}
|
||||
style={{ boxShadow: `0 0 0 4px ${cssVar.colorBgContainer}`, flexShrink: 0 }}
|
||||
/>
|
||||
<Avatar avatar={avatarUrl} shape={'square'} size={64} style={styles.flexContainer} />
|
||||
<Flexbox align={'flex-start'} gap={16} horizontal justify={'space-between'}>
|
||||
<Flexbox
|
||||
gap={4}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Text as={'h1'} ellipsis fontSize={24} style={{ margin: 0 }} weight={'bold'}>
|
||||
<Flexbox gap={4} style={styles.style}>
|
||||
<Text as={'h1'} ellipsis fontSize={24} style={styles.spacing} weight={'bold'}>
|
||||
{displayName}
|
||||
</Text>
|
||||
<Text ellipsis fontSize={12} type={'secondary'}>
|
||||
|
||||
@@ -5,20 +5,24 @@ import { cssVar } from 'antd-style';
|
||||
import { memo } from 'react';
|
||||
|
||||
import ListLoading from '@/app/[variants]/(main)/community/components/ListLoading';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import Banner from './features/Header/Banner';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
flexContainer: {
|
||||
boxShadow: `0 0 0 4px ${cssVar.colorBgContainer}`,
|
||||
flexShrink: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const Loading = memo(() => {
|
||||
return (
|
||||
<Flexbox gap={24} width={'100%'}>
|
||||
{/* User Header Skeleton */}
|
||||
<Banner />
|
||||
<Flexbox gap={16}>
|
||||
<Skeleton.Avatar
|
||||
shape={'square'}
|
||||
size={64}
|
||||
style={{ boxShadow: `0 0 0 4px ${cssVar.colorBgContainer}`, flexShrink: 0 }}
|
||||
/>
|
||||
<Skeleton.Avatar shape={'square'} size={64} style={styles.flexContainer} />
|
||||
<Skeleton paragraph={{ rows: 1 }} />
|
||||
</Flexbox>
|
||||
|
||||
|
||||
@@ -10,10 +10,18 @@ import { withSuspense } from '@/components/withSuspense';
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { useDiscoverStore } from '@/store/discover';
|
||||
import { AssistantCategory } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import CategoryMenu from '../../../../components/CategoryMenu';
|
||||
import { useCategory } from './useCategory';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
},
|
||||
});
|
||||
|
||||
const Category = memo(() => {
|
||||
const useAssistantCategories = useDiscoverStore((s) => s.useAssistantCategories);
|
||||
const {
|
||||
@@ -51,24 +59,12 @@ const Category = memo(() => {
|
||||
extra:
|
||||
item.key === 'all'
|
||||
? total > 0 && (
|
||||
<Tag
|
||||
size={'small'}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
}}
|
||||
>
|
||||
<Tag size={'small'} style={styles.spacing}>
|
||||
{total}
|
||||
</Tag>
|
||||
)
|
||||
: itemData && (
|
||||
<Tag
|
||||
size={'small'}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
}}
|
||||
>
|
||||
<Tag size={'small'} style={styles.spacing}>
|
||||
{itemData.count}
|
||||
</Tag>
|
||||
),
|
||||
|
||||
@@ -11,12 +11,23 @@ import { useCategory } from '@/hooks/useMCPCategory';
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { useDiscoverStore } from '@/store/discover';
|
||||
import { McpCategory, McpSorts } from '@/types/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import CategoryMenu from '../../../../components/CategoryMenu';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
},
|
||||
});
|
||||
|
||||
const Category = memo(() => {
|
||||
const useMcpCategories = useDiscoverStore((s) => s.useMcpCategories);
|
||||
const { category = McpCategory.Discover, q } = useQuery() as { category?: McpCategory; q?: string };
|
||||
const { category = McpCategory.Discover, q } = useQuery() as {
|
||||
category?: McpCategory;
|
||||
q?: string;
|
||||
};
|
||||
const { data: items = [] } = useMcpCategories({ q });
|
||||
const navigate = useNavigate();
|
||||
const cates = useCategory();
|
||||
@@ -50,24 +61,12 @@ const Category = memo(() => {
|
||||
extra:
|
||||
item.key === 'all'
|
||||
? total > 0 && (
|
||||
<Tag
|
||||
size={'small'}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
}}
|
||||
>
|
||||
<Tag size={'small'} style={styles.spacing}>
|
||||
{total}
|
||||
</Tag>
|
||||
)
|
||||
: itemData && (
|
||||
<Tag
|
||||
size={'small'}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
}}
|
||||
>
|
||||
<Tag size={'small'} style={styles.spacing}>
|
||||
{itemData.count}
|
||||
</Tag>
|
||||
),
|
||||
|
||||
@@ -4,6 +4,15 @@ import { Blend, Cloud, LaptopMinimalIcon } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
colored: {
|
||||
color: cssVar.colorTextSecondary,
|
||||
fontSize: 12,
|
||||
},
|
||||
});
|
||||
|
||||
interface ConnectionTypeTagProps {
|
||||
type?: 'hybrid' | 'local' | 'remote';
|
||||
}
|
||||
@@ -30,15 +39,7 @@ const ConnectionTypeTag = memo<ConnectionTypeTagProps>(({ type }) => {
|
||||
|
||||
return (
|
||||
<Tooltip title={t(`mcp.details.connectionType.${type}.desc`)}>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
gap={6}
|
||||
horizontal
|
||||
style={{
|
||||
color: cssVar.colorTextSecondary,
|
||||
fontSize: 12,
|
||||
}}
|
||||
>
|
||||
<Flexbox align={'center'} gap={6} horizontal style={styles.colored}>
|
||||
<Icon color={icons[type].color} icon={icons[type].icon} size={14} />
|
||||
{t(`mcp.details.connectionType.${type}.title`)}
|
||||
</Flexbox>
|
||||
|
||||
@@ -9,10 +9,18 @@ import { SCROLL_PARENT_ID } from '@/app/[variants]/(main)/community/features/con
|
||||
import { withSuspense } from '@/components/withSuspense';
|
||||
import { useQuery } from '@/hooks/useQuery';
|
||||
import { useDiscoverStore } from '@/store/discover';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
import CategoryMenu from '../../../../components/CategoryMenu';
|
||||
import { useCategory } from './useCategory';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
},
|
||||
});
|
||||
|
||||
const Category = memo(() => {
|
||||
const useModelCategories = useDiscoverStore((s) => s.useModelCategories);
|
||||
const { category = 'all', q } = useQuery() as { category?: string; q?: string };
|
||||
@@ -45,24 +53,12 @@ const Category = memo(() => {
|
||||
extra:
|
||||
item.key === 'all'
|
||||
? total > 0 && (
|
||||
<Tag
|
||||
size={'small'}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
}}
|
||||
>
|
||||
<Tag size={'small'} style={styles.spacing}>
|
||||
{total}
|
||||
</Tag>
|
||||
)
|
||||
: itemData && (
|
||||
<Tag
|
||||
size={'small'}
|
||||
style={{
|
||||
borderRadius: 12,
|
||||
paddingInline: 6,
|
||||
}}
|
||||
>
|
||||
<Tag size={'small'} style={styles.spacing}>
|
||||
{itemData.count}
|
||||
</Tag>
|
||||
),
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
import { ScrollShadow } from '@lobehub/ui';
|
||||
import { type FC, type PropsWithChildren } from 'react';
|
||||
import { type FC, type PropsWithChildren, useMemo } from 'react';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
spacing: {
|
||||
paddingBottom: 16,
|
||||
position: 'sticky',
|
||||
},
|
||||
});
|
||||
|
||||
const CategoryContainer: FC<PropsWithChildren<{ top?: number }>> = ({ children, top = 16 }) => {
|
||||
const dynamicStyles = useMemo(
|
||||
() => ({
|
||||
...styles.spacing,
|
||||
top,
|
||||
}),
|
||||
[top],
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollShadow
|
||||
as={'aside'}
|
||||
@@ -10,7 +27,7 @@ const CategoryContainer: FC<PropsWithChildren<{ top?: number }>> = ({ children,
|
||||
hideScrollBar
|
||||
offset={16}
|
||||
size={4}
|
||||
style={{ paddingBottom: 16, position: 'sticky', top }}
|
||||
style={dynamicStyles}
|
||||
width={280}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -3,6 +3,17 @@ import { Bot } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
minHeight: '50vh',
|
||||
},
|
||||
style1: {
|
||||
maxWidth: 400,
|
||||
},
|
||||
});
|
||||
|
||||
interface AssistantEmptyProps extends Omit<EmptyProps, 'icon'> {
|
||||
search?: boolean;
|
||||
}
|
||||
@@ -11,16 +22,14 @@ const AssistantEmpty = memo<AssistantEmptyProps>(({ search, ...rest }) => {
|
||||
const { t } = useTranslation('discover');
|
||||
|
||||
return (
|
||||
<Center height="100%" style={{ minHeight: '50vh' }} width="100%">
|
||||
<Center height="100%" style={styles.style} width="100%">
|
||||
<Empty
|
||||
description={search ? t('assistants.empty.search') : t('assistants.empty.description')}
|
||||
descriptionProps={{
|
||||
fontSize: 14,
|
||||
}}
|
||||
icon={Bot}
|
||||
style={{
|
||||
maxWidth: 400,
|
||||
}}
|
||||
style={styles.style1}
|
||||
title={search ? undefined : t('assistants.empty.title')}
|
||||
type={search ? 'default' : 'page'}
|
||||
{...rest}
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import { Button, Icon, Tag, Typography } from '@lobehub/ui';
|
||||
import { Divider } from 'antd';
|
||||
import { Github, Settings, Share2 } from 'lucide-react';
|
||||
import Image from '@/libs/next/Image';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { AGENTS_INDEX_GITHUB, imageUrl } from '@/const/url';
|
||||
import Image from '@/libs/next/Image';
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
fullWidth: {
|
||||
height: 'auto',
|
||||
marginBottom: 24,
|
||||
width: '100%',
|
||||
},
|
||||
});
|
||||
|
||||
const Inner = memo(() => {
|
||||
const { t } = useTranslation('discover');
|
||||
@@ -15,7 +24,7 @@ const Inner = memo(() => {
|
||||
alt={'banner'}
|
||||
height={602}
|
||||
src={imageUrl('banner_market_modal.webp')}
|
||||
style={{ height: 'auto', marginBottom: 24, width: '100%' }}
|
||||
style={styles.fullWidth}
|
||||
width={1602}
|
||||
/>
|
||||
<h3>
|
||||
|
||||
@@ -3,6 +3,17 @@ import { Boxes } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
minHeight: '50vh',
|
||||
},
|
||||
style1: {
|
||||
maxWidth: 400,
|
||||
},
|
||||
});
|
||||
|
||||
interface McpEmptyProps extends Omit<EmptyProps, 'icon'> {
|
||||
search?: boolean;
|
||||
}
|
||||
@@ -11,16 +22,14 @@ const McpEmpty = memo<McpEmptyProps>(({ search, ...rest }) => {
|
||||
const { t } = useTranslation('discover');
|
||||
|
||||
return (
|
||||
<Center height="100%" style={{ minHeight: '50vh' }} width="100%">
|
||||
<Center height="100%" style={styles.style} width="100%">
|
||||
<Empty
|
||||
description={search ? t('mcpEmpty.search') : t('mcpEmpty.description')}
|
||||
descriptionProps={{
|
||||
fontSize: 14,
|
||||
}}
|
||||
icon={Boxes}
|
||||
style={{
|
||||
maxWidth: 400,
|
||||
}}
|
||||
style={styles.style1}
|
||||
title={search ? undefined : t('mcpEmpty.title')}
|
||||
type={search ? 'default' : 'page'}
|
||||
{...rest}
|
||||
|
||||
@@ -3,6 +3,17 @@ import { Cpu } from 'lucide-react';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { StyleSheet } from '@/utils/styles';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
style: {
|
||||
minHeight: '50vh',
|
||||
},
|
||||
style1: {
|
||||
maxWidth: 400,
|
||||
},
|
||||
});
|
||||
|
||||
interface ModelEmptyProps extends Omit<EmptyProps, 'icon'> {
|
||||
search?: boolean;
|
||||
}
|
||||
@@ -11,16 +22,14 @@ const ModelEmpty = memo<ModelEmptyProps>(({ search, ...rest }) => {
|
||||
const { t } = useTranslation('discover');
|
||||
|
||||
return (
|
||||
<Center height="100%" style={{ minHeight: '50vh' }} width="100%">
|
||||
<Center height="100%" style={styles.style} width="100%">
|
||||
<Empty
|
||||
description={search ? t('models.empty.search') : t('models.empty.description')}
|
||||
descriptionProps={{
|
||||
fontSize: 14,
|
||||
}}
|
||||
icon={Cpu}
|
||||
style={{
|
||||
maxWidth: 400,
|
||||
}}
|
||||
style={styles.style1}
|
||||
title={search ? undefined : t('models.empty.title')}
|
||||
type={search ? 'default' : 'page'}
|
||||
{...rest}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user