fix: auth button hover, nav links, minimal homepage redesign
Docker Build / Build and Push Docker Image (push) Successful in 4m18s

This commit is contained in:
2026-06-14 17:19:03 +08:00
parent 2d8bdc1b7c
commit 5eeb3c9f18
6 changed files with 51 additions and 110 deletions
@@ -227,7 +227,7 @@ export function PublicHeader(props: PublicHeaderProps) {
tabIndex={link.disabled ? -1 : undefined}
onClick={(event) => handleNavLinkClick(event, link)}
className={cn(
'text-[#8B8D97] hover:text-white rounded-lg px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
'text-[#8B8D97] hover:text-[#C5C6D0] rounded-md px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
link.disabled && 'pointer-events-none opacity-50'
)}
>
@@ -242,14 +242,17 @@ export function PublicHeader(props: PublicHeaderProps) {
disabled={link.disabled}
onClick={(event) => handleNavLinkClick(event, link)}
className={cn(
'rounded-lg px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
'relative rounded-md px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
isActive
? 'text-white'
: 'text-[#8B8D97] hover:text-white',
? 'text-[#00D2FF]'
: 'text-[#8B8D97] hover:text-[#C5C6D0]',
link.disabled && 'pointer-events-none opacity-50'
)}
>
{t(link.title)}
{isActive && (
<span className='absolute bottom-0 left-3 right-3 h-[2px] rounded-full bg-[#00D2FF]' />
)}
</Link>
)
})}
@@ -112,7 +112,7 @@ export function ForgotPasswordForm({
<Button
type='submit'
className='mt-2 bg-[#00D2FF] text-[#0A0E14] hover:bg-[#00B8E6]'
className='mt-2 !bg-[#00D2FF] !text-[#0A0E14] hover:!bg-[#00B8E6]'
disabled={isLoading || isActive || !turnstileReady}
>
{isActive
+1 -1
View File
@@ -203,7 +203,7 @@ export function OtpForm({ className, ...props }: OtpFormProps) {
<Button
type='submit'
className='mt-2 w-full bg-[#00D2FF] text-[#0A0E14] hover:bg-[#00B8E6]'
className='mt-2 w-full !bg-[#00D2FF] !text-[#0A0E14] hover:!bg-[#00B8E6]'
disabled={!isFormValid || isLoading}
>
{isLoading ? <Loader2 className='h-4 w-4 animate-spin' /> : null}
@@ -374,7 +374,7 @@ export function UserAuthForm({
{/* Submit Button */}
<Button
type='submit'
className='mt-2 w-full justify-center gap-2 bg-[#00D2FF] text-[#0A0E14] hover:bg-[#00B8E6]'
className='mt-2 w-full justify-center gap-2 !bg-[#00D2FF] !text-[#0A0E14] hover:!bg-[#00B8E6]'
disabled={isLoading || (requiresLegalConsent && !agreedToLegal)}
>
{isLoading ? <Loader2 className='animate-spin' /> : <LogIn />}
@@ -354,7 +354,7 @@ export function SignUpForm({
{/* Submit Button */}
<Button
type='submit'
className='mt-2 w-full justify-center gap-2 bg-[#00D2FF] text-[#0A0E14] hover:bg-[#00B8E6]'
className='mt-2 w-full justify-center gap-2 !bg-[#00D2FF] !text-[#0A0E14] hover:!bg-[#00B8E6]'
disabled={
isLoading ||
(requiresLegalConsent && !agreedToLegal) ||
+40 -102
View File
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact admin@modelstoken.com
*/
import { Link } from '@tanstack/react-router'
import { ArrowRight, Zap, Shield, Layers, BarChart3, Key, GitBranch } from 'lucide-react'
import { ArrowRight } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { useStatus } from '@/hooks/use-status'
import { Button } from '@/components/ui/button'
@@ -27,13 +27,11 @@ interface HeroProps {
isAuthenticated?: boolean
}
/* Palette: black bg, cyan accent, gray layers */
const T = {
bg: '#0A0E14',
surface: '#0F1419',
border: '#1A1F29',
accent: '#00D2FF',
accentDim: '#0099BB',
green: '#28C840',
yellow: '#FEBC2E',
gray: '#5C6370',
@@ -42,15 +40,6 @@ const T = {
red: '#FF5F57',
}
const features = [
{ icon: Zap, title: 'Multi-model Routing', desc: 'Intelligent routing with automatic failover and load balancing across providers' },
{ icon: Key, title: 'Key Management', desc: 'Centralized API key lifecycle management with usage tracking and rotation' },
{ icon: Shield, title: 'Access Control', desc: 'Fine-grained permissions, rate limiting, and token-level access policies' },
{ icon: BarChart3, title: 'Real-time Monitoring', desc: 'Live dashboards with request latency, token usage, and cost analytics' },
{ icon: Layers, title: 'OpenAI Compatible', desc: 'Drop-in replacement for OpenAI API — no code changes required' },
{ icon: GitBranch, title: 'Model Mapping', desc: 'Map model names, override parameters, and customize per-channel behavior' },
]
export function Hero(props: HeroProps) {
const { t } = useTranslation()
const { status } = useStatus()
@@ -60,28 +49,22 @@ export function Hero(props: HeroProps) {
return (
<section className='relative overflow-hidden' style={{ backgroundColor: T.bg }}>
{/* Top glow */}
{/* Subtle top glow */}
<div
aria-hidden
className='pointer-events-none absolute inset-0'
style={{
background:
'radial-gradient(ellipse 50% 30% at 50% 0%, rgba(0,210,255,0.07) 0%, transparent 70%)',
'radial-gradient(ellipse 40% 25% at 50% 0%, rgba(0,210,255,0.06) 0%, transparent 70%)',
}}
/>
<div className='relative mx-auto max-w-5xl px-6'>
<div className='relative mx-auto max-w-4xl px-6'>
{/* ── Hero ── */}
<div className='flex min-h-[calc(100svh-3rem)] flex-col items-center justify-center py-20 text-center'>
{/* Eyebrow */}
<div className='mb-8 inline-flex items-center gap-2 rounded-full border px-3.5 py-1 text-xs' style={{ borderColor: T.border, backgroundColor: T.surface, color: T.grayLight }}>
<span className='inline-block size-1.5 rounded-full' style={{ backgroundColor: T.accent }} />
{t('Compatible with OpenAI API')}
</div>
{/* Headline */}
<h1
className='max-w-2xl text-[clamp(2.2rem,6vw,4rem)] leading-[1.05] font-bold tracking-[-0.03em]'
className='max-w-xl text-[clamp(2.4rem,7vw,4.5rem)] leading-[1] font-bold tracking-[-0.04em]'
style={{ color: T.white }}
>
{t('Unified LLM')}{' '}
@@ -89,12 +72,12 @@ export function Hero(props: HeroProps) {
</h1>
{/* Subtitle */}
<p className='mt-6 max-w-md text-base leading-relaxed' style={{ color: T.grayLight }}>
<p className='mt-6 max-w-sm text-[15px] leading-relaxed' style={{ color: T.grayLight }}>
{t('One endpoint for all models. OpenAI-compatible, switch and go.')}
</p>
{/* CTA */}
<div className='mt-10 flex flex-wrap items-center justify-center gap-3'>
<div className='mt-10 flex items-center gap-4'>
{props.isAuthenticated ? (
<Button
className='group h-11 rounded-lg px-7 text-sm font-semibold hover:opacity-90'
@@ -114,123 +97,78 @@ export function Hero(props: HeroProps) {
{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='h-11 rounded-lg px-7 text-sm font-medium hover:opacity-90'
style={{ borderColor: T.border, backgroundColor: 'transparent', color: T.white }}
render={<Link to='/pricing' />}
<Link
to='/pricing'
className='text-[13px] font-medium transition-colors duration-200'
style={{ color: T.grayLight }}
onMouseEnter={(e) => { e.currentTarget.style.color = T.accent }}
onMouseLeave={(e) => { e.currentTarget.style.color = T.grayLight }}
>
{t('View Pricing')}
</Button>
{t('View Pricing')}
</Link>
</>
)}
</div>
</div>
{/* ── Terminal demo ── */}
<div className='-mt-8 pb-20'>
<div className='-mt-4 pb-28'>
<div
className='mx-auto w-full max-w-2xl overflow-hidden rounded-xl border shadow-[0_16px_64px_-16px_rgba(0,0,0,0.5)]'
className='mx-auto w-full max-w-xl overflow-hidden rounded-xl border'
style={{ borderColor: T.border, backgroundColor: T.surface }}
>
{/* Terminal header */}
<div className='flex items-center gap-2 border-b px-4 py-2.5' style={{ borderColor: T.border }}>
<div className='flex items-center gap-2 border-b px-4 py-2' style={{ borderColor: T.border }}>
<div className='flex gap-1.5'>
<div className='size-2.5 rounded-full' style={{ backgroundColor: T.red }} />
<div className='size-2.5 rounded-full' style={{ backgroundColor: T.yellow }} />
<div className='size-2.5 rounded-full' style={{ backgroundColor: T.green }} />
<div className='size-2 rounded-full' style={{ backgroundColor: T.red }} />
<div className='size-2 rounded-full' style={{ backgroundColor: T.yellow }} />
<div className='size-2 rounded-full' style={{ backgroundColor: T.green }} />
</div>
<span className='ml-2 text-[11px]' style={{ color: T.gray }}>bash</span>
<span className='ml-2 text-[10px]' style={{ color: T.gray }}>bash</span>
</div>
{/* Terminal content */}
<div className='p-5 font-mono text-[13px] leading-[1.9]'>
<div className='p-4 font-mono text-[12px] leading-[2]'>
<div>
<span style={{ color: T.accent }}>$</span>{' '}
<span style={{ color: T.white }}>curl</span>{' '}
<span style={{ color: T.accent }}>{serverAddress}/v1/chat/completions</span>{' '}
<span style={{ color: T.gray }}>\</span>
</div>
<div className='pl-5'>
<div className='pl-4'>
<span style={{ color: T.gray }}>-H</span>{' '}
<span style={{ color: T.yellow }}>"Authorization: Bearer sk-..."</span>{' '}
<span style={{ color: T.gray }}>\</span>
</div>
<div className='pl-5'>
<div className='pl-4'>
<span style={{ color: T.gray }}>-d</span>{' '}
<span style={{ color: T.yellow }}>{"'{ \"model\": \"gpt-4o\", \"messages\": [...] }'"}</span>
<span style={{ color: T.yellow }}>{"'{ \"model\": \"gpt-4o\" }'"}</span>
</div>
<div className='mt-5 border-t pt-4' style={{ borderColor: T.border }}>
<div className='flex items-center gap-2 mb-3'>
<div className='mt-4 border-t pt-3' style={{ borderColor: T.border }}>
<div className='flex items-center gap-2 mb-2'>
<span className='inline-block size-1.5 rounded-full' style={{ backgroundColor: T.green }} />
<span className='text-[11px] font-medium' style={{ color: T.gray }}>200 OK</span>
<span className='text-[11px]' style={{ color: T.gray }}>312ms</span>
<span className='text-[10px] font-medium' style={{ color: T.gray }}>200</span>
<span className='text-[10px]' style={{ color: T.gray }}>312ms</span>
</div>
<div>
<span style={{ color: T.gray }}>{"{"}</span>{' '}
<span style={{ color: T.accent }}>"model"</span>
<span style={{ color: T.gray }}>:</span>{' '}
<span style={{ color: T.yellow }}>"gpt-4o"</span>
<span style={{ color: T.gray }}>,</span>{' '}
<span style={{ color: T.accent }}>"usage"</span>
<span style={{ color: T.gray }}>:</span>{' '}
<span style={{ color: T.gray }}>{"{ \"prompt_tokens\": 12, \"completion_tokens\": 47 }"}</span>
<span style={{ color: T.gray }}>{"{"}</span>
<span style={{ color: T.accent }}> "model"</span>
<span style={{ color: T.gray }}>:</span>
<span style={{ color: T.yellow }}> "gpt-4o"</span>
<span style={{ color: T.gray }}>,</span>
<span style={{ color: T.accent }}> "usage"</span>
<span style={{ color: T.gray }}>:</span>
<span style={{ color: T.gray }}> {"{...}"} </span>
<span style={{ color: T.gray }}>{"}"}</span>
</div>
</div>
<div className='mt-3'>
<div className='mt-2'>
<span style={{ color: T.accent }}>$</span>
<span className='ml-1 inline-block h-3.5 w-[7px] animate-pulse align-middle' style={{ backgroundColor: T.accent }} />
<span className='ml-1 inline-block h-3 w-[6px] animate-pulse align-middle' style={{ backgroundColor: T.accent }} />
</div>
</div>
</div>
</div>
{/* ── Features ── */}
<div className='pb-28'>
<div className='mb-14 text-center'>
<h2 className='text-2xl font-semibold tracking-tight' style={{ color: T.white }}>
{t('Full-featured gateway')}
</h2>
<p className='mt-3 text-sm' style={{ color: T.grayLight }}>
{t('Everything you need to manage, route, and monitor LLM API traffic')}
</p>
</div>
<div className='grid gap-5 sm:grid-cols-2 lg:grid-cols-3'>
{features.map((feature) => (
<div
key={feature.title}
className='group rounded-xl border p-6 transition-all duration-200'
style={{
borderColor: T.border,
backgroundColor: T.surface,
}}
onMouseEnter={(e) => {
e.currentTarget.style.borderColor = T.accentDim
e.currentTarget.style.transform = 'translateY(-2px)'
}}
onMouseLeave={(e) => {
e.currentTarget.style.borderColor = T.border
e.currentTarget.style.transform = 'translateY(0)'
}}
>
<div
className='mb-4 flex size-10 items-center justify-center rounded-lg'
style={{ backgroundColor: `${T.accent}12` }}
>
<feature.icon className='size-5' style={{ color: T.accent }} />
</div>
<h3 className='text-sm font-semibold' style={{ color: T.white }}>
{t(feature.title)}
</h3>
<p className='mt-2 text-[13px] leading-relaxed' style={{ color: T.gray }}>
{t(feature.desc)}
</p>
</div>
))}
</div>
</div>
</div>
</section>
)