refactor(home): redesign hero section to dual-column layout with compliant copywriting

Redesigns the hero section into a balanced horizontal dual-column layout:
- Left Column: Features title, clean legal-compliant descriptions, CTA buttons with BookOpen Docs link, and enlarged supported apps buttons (Cherry Studio and CC Switch with lobe icons)
- Right Column: Smoothly integrates the terminal API demo with top horizontal alignment
- i18n: Configures compliance translations for en, zh, fr, ja, ru, and vi locales
This commit is contained in:
CaIon
2026-05-25 23:10:10 +08:00
parent 1288028181
commit 51ca897cf4
8 changed files with 305 additions and 126 deletions
@@ -163,7 +163,11 @@ const API_DEMOS: ApiDemoConfig[] = [
const CYCLE_INTERVAL = 4500
const TRANSITION_MS = 220
export function HeroTerminalDemo() {
interface HeroTerminalDemoProps {
className?: string
}
export function HeroTerminalDemo(props: HeroTerminalDemoProps) {
const [activeIndex, setActiveIndex] = useState(0)
const [transitioning, setTransitioning] = useState(false)
const intervalRef = useRef<ReturnType<typeof setInterval>>(undefined)
@@ -202,7 +206,7 @@ export function HeroTerminalDemo() {
const accent = ACCENT_CLASSES[demo.accent]
return (
<div className='mx-auto mt-16 w-full max-w-2xl'>
<div className={cn('mx-auto w-full max-w-2xl', props.className)}>
<div
className={cn(
'overflow-hidden rounded-2xl border backdrop-blur-sm',
+191 -58
View File
@@ -17,9 +17,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { Link } from '@tanstack/react-router'
import { ArrowRight } from 'lucide-react'
import { ArrowRight, BookOpen } from 'lucide-react'
import { CherryStudio } from '@lobehub/icons'
import { useTranslation } from 'react-i18next'
import { Button } from '@/components/ui/button'
import { useStatus } from '@/hooks/use-status'
import { HeroTerminalDemo } from '../hero-terminal-demo'
interface HeroProps {
@@ -27,11 +29,59 @@ interface HeroProps {
isAuthenticated?: boolean
}
// Stylized three-dots indicator representing "More"
const MoreIcon = () => (
<svg
className='size-6 shrink-0 text-muted-foreground/60 transition-colors group-hover:text-foreground'
viewBox='0 0 24 24'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<circle cx='6' cy='12' r='2' fill='currentColor' />
<circle cx='12' cy='12' r='2' fill='currentColor' />
<circle cx='18' cy='12' r='2' fill='currentColor' />
</svg>
)
export function Hero(props: HeroProps) {
const { t } = useTranslation()
const { status } = useStatus()
const docsUrl = (status?.docs_link as string | undefined) || 'https://docs.newapi.pro'
const renderDocsButton = () => {
const isExternal = docsUrl.startsWith('http')
if (isExternal) {
return (
<Button
variant='outline'
className='group border-border/50 hover:border-border hover:bg-muted/50 rounded-lg h-11 px-5 text-sm font-medium inline-flex items-center gap-1.5'
render={
<a
href={docsUrl}
target='_blank'
rel='noopener noreferrer'
/>
}
>
<BookOpen className='size-4 text-muted-foreground/80 group-hover:text-foreground transition-colors duration-200' />
<span>{t('Docs')}</span>
</Button>
)
}
return (
<Button
variant='outline'
className='group border-border/50 hover:border-border hover:bg-muted/50 rounded-lg h-11 px-5 text-sm font-medium inline-flex items-center gap-1.5'
render={<Link to={docsUrl} />}
>
<BookOpen className='size-4 text-muted-foreground/80 group-hover:text-foreground transition-colors duration-200' />
<span>{t('Docs')}</span>
</Button>
)
}
return (
<section className='relative z-10 flex flex-col items-center overflow-hidden px-6 pt-28 pb-16 md:pt-36 md:pb-24'>
<section className='relative z-10 overflow-hidden px-6 pt-24 pb-16 md:pt-32 md:pb-24 lg:pt-36 lg:pb-28'>
{/* Radial gradient background */}
<div
aria-hidden
@@ -50,63 +100,146 @@ export function Hero(props: HeroProps) {
className='absolute inset-0 -z-10 bg-[linear-gradient(to_right,var(--border)_1px,transparent_1px),linear-gradient(to_bottom,var(--border)_1px,transparent_1px)] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_30%,black_20%,transparent_100%)] bg-[size:4rem_4rem] opacity-[0.08]'
/>
<div className='flex max-w-3xl flex-col items-center text-center'>
<h1
className='landing-animate-fade-up text-[clamp(2rem,5.5vw,3.5rem)] leading-[1.15] font-bold tracking-tight'
style={{ animationDelay: '0ms' }}
>
{t('Unified API Gateway for')}
<br />
<span className='bg-gradient-to-r from-blue-400 via-violet-400 to-purple-500 bg-clip-text text-transparent'>
{t('All Your AI Models')}
</span>
</h1>
<p
className='landing-animate-fade-up text-muted-foreground/80 mt-5 max-w-lg text-base leading-relaxed opacity-0 md:text-lg'
style={{ animationDelay: '80ms' }}
>
{t(
'Power AI applications, manage digital assets, connect the Future'
)}
</p>
<div
className='landing-animate-fade-up mt-8 flex items-center gap-3 opacity-0'
style={{ animationDelay: '160ms' }}
>
{props.isAuthenticated ? (
<Button
className='group rounded-lg'
render={<Link to='/dashboard' />}
>
{t('Go to Dashboard')}
<ArrowRight className='ml-1 size-3.5 transition-transform duration-200 group-hover:translate-x-0.5' />
</Button>
) : (
<>
<Button
className='group rounded-lg'
render={<Link to='/sign-up' />}
>
{t('Get Started')}
<ArrowRight className='ml-1 size-3.5 transition-transform duration-200 group-hover:translate-x-0.5' />
</Button>
<Button
variant='outline'
className='border-border/50 hover:border-border hover:bg-muted/50 rounded-lg'
render={<Link to='/pricing' />}
>
{t('View Pricing')}
</Button>
</>
)}
</div>
</div>
<div className='mx-auto grid max-w-6xl grid-cols-1 items-start gap-12 lg:grid-cols-12 lg:gap-8'>
{/* Left Column: Title, description, action buttons and application support */}
<div className='flex flex-col items-start text-left lg:col-span-6'>
{/* Top Pill Badge */}
<div
className='landing-animate-fade-up mb-5 inline-flex items-center gap-1.5 rounded-full border border-blue-500/20 bg-blue-500/5 px-3 py-1.5 text-[11px] font-medium text-blue-600 dark:border-blue-400/20 dark:bg-blue-400/5 dark:text-blue-400 opacity-0 shadow-xs'
style={{ animationDelay: '0ms' }}
>
<span className='relative flex size-1.5'>
<span className='absolute inline-flex h-full w-full animate-ping rounded-full bg-blue-400 opacity-75' />
<span className='relative inline-flex size-1.5 rounded-full bg-blue-500 dark:bg-blue-400' />
</span>
<span>{t('AI Application Infrastructure Foundation')}</span>
</div>
<div
className='landing-animate-fade-up w-full opacity-0'
style={{ animationDelay: '300ms' }}
>
<HeroTerminalDemo />
<h1
className='landing-animate-fade-up text-[clamp(2.25rem,4.5vw,3.25rem)] leading-[1.15] font-bold tracking-tight'
style={{ animationDelay: '60ms' }}
>
{t('Unified API Gateway for')}
<br />
<span className='bg-gradient-to-r from-blue-400 via-violet-400 to-purple-500 bg-clip-text text-transparent'>
{t('Vast Range of AI Models')}
</span>
</h1>
<p
className='landing-animate-fade-up text-muted-foreground/80 mt-5 max-w-xl text-base leading-relaxed opacity-0 md:text-[15px]'
style={{ animationDelay: '120ms' }}
>
{t(
'Access a vast selection of models via a standard, unified API protocol. Power AI applications, manage digital assets, and connect the Future.'
)}
</p>
<div
className='landing-animate-fade-up mt-8 flex flex-wrap items-center gap-3 opacity-0'
style={{ animationDelay: '180ms' }}
>
{props.isAuthenticated ? (
<>
<Button
className='group rounded-lg h-11 px-5 text-sm font-medium'
render={<Link to='/dashboard' />}
>
{t('Go to Dashboard')}
<ArrowRight className='ml-1.5 size-4 transition-transform duration-200 group-hover:translate-x-0.5' />
</Button>
{renderDocsButton()}
</>
) : (
<>
<Button
className='group rounded-lg h-11 px-5 text-sm font-medium'
render={<Link to='/sign-up' />}
>
{t('Get Started')}
<ArrowRight className='ml-1.5 size-4 transition-transform duration-200 group-hover:translate-x-0.5' />
</Button>
<Button
variant='outline'
className='border-border/50 hover:border-border hover:bg-muted/50 rounded-lg h-11 px-5 text-sm font-medium'
render={<Link to='/pricing' />}
>
{t('View Pricing')}
</Button>
{renderDocsButton()}
</>
)}
</div>
{/* Supported Apps (参考图二样式,进行卡片化和信息扩充设计,增加视觉高度) */}
<div
className='landing-animate-fade-up mt-10 w-full max-w-xl opacity-0'
style={{ animationDelay: '240ms' }}
>
<div className='flex flex-col gap-1 mb-4'>
<span className='text-[10px] font-bold tracking-[0.15em] text-muted-foreground/50 uppercase'>
{t('Supported Applications')}
</span>
<p className='text-xs text-muted-foreground/60 leading-relaxed'>
{t('Supports one-click configuration and perfectly adapts to NewAPI multi-protocol configuration.')}
</p>
</div>
<div className='flex flex-wrap items-center gap-3'>
{/* Cherry Studio */}
<a
href='https://cherry-ai.com'
target='_blank'
rel='noopener noreferrer'
className='group flex items-center gap-3 rounded-full border border-border/40 bg-muted/15 px-5 py-2.5 text-sm font-medium text-foreground/80 shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:border-border hover:bg-muted/30 hover:text-foreground hover:scale-[1.02]'
>
<CherryStudio.Color size={24} className='shrink-0' />
<span>Cherry Studio</span>
</a>
{/* CC Switch */}
<a
href='https://ccswitch.io'
target='_blank'
rel='noopener noreferrer'
className='group flex items-center gap-3 rounded-full border border-border/40 bg-muted/15 px-5 py-2.5 text-sm font-medium text-foreground/80 shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:border-border hover:bg-muted/30 hover:text-foreground hover:scale-[1.02]'
>
<img
src='https://ccswitch.io/favicon.png'
alt='CC Switch'
className='size-6 shrink-0 rounded-md object-contain'
onError={(e) => {
// Fallback to a styled text avatar if the remote favicon fails to load in sandbox or local environments
e.currentTarget.style.display = 'none'
const fallback = e.currentTarget.nextSibling as HTMLElement
if (fallback) fallback.style.display = 'flex'
}}
/>
<span
style={{ display: 'none' }}
className='size-6 shrink-0 items-center justify-center rounded-md bg-blue-500/10 text-[10px] font-bold text-blue-600 dark:bg-blue-400/10 dark:text-blue-400'
>
CC
</span>
<span>CC Switch</span>
</a>
{/* "更多" */}
<div
className='group flex items-center gap-2.5 rounded-full border border-border/40 bg-muted/15 px-5 py-2.5 text-sm font-medium text-foreground/55 shadow-[0_1px_2.5px_rgba(0,0,0,0.01)] backdrop-blur-xs transition-all duration-300 hover:border-border hover:bg-muted/30 hover:text-foreground hover:scale-[1.02] cursor-default'
>
<MoreIcon />
<span>{t('More Apps')}</span>
</div>
</div>
</div>
</div>
{/* Right Column: Hero Terminal API Demo */}
<div
className='landing-animate-fade-up flex justify-center w-full opacity-0 lg:col-span-6'
style={{ animationDelay: '320ms' }}
>
<HeroTerminalDemo className='mt-8 lg:mt-0' />
</div>
</div>
</section>
)