fix: light mode button colors, redesign homepage with features grid
Docker Build / Build and Push Docker Image (push) Successful in 4m13s

This commit is contained in:
2026-06-14 15:05:00 +08:00
parent 603886d422
commit 60d63f87ff
6 changed files with 124 additions and 54 deletions
+2 -2
View File
@@ -34,7 +34,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
export function LanguageSwitcher() {
export function LanguageSwitcher({ className }: { className?: string }) {
const { i18n, t } = useTranslation()
const user = useAuthStore((s) => s.auth.user)
const currentLanguage = normalizeInterfaceLanguage(i18n.language)
@@ -56,7 +56,7 @@ export function LanguageSwitcher() {
return (
<DropdownMenu modal={false}>
<DropdownMenuTrigger
render={<Button variant='ghost' size='icon' className='h-9 w-9' />}
render={<Button variant='ghost' size='icon' className={cn('h-9 w-9', className)} />}
>
<Languages className='size-[1.2rem]' />
<span className='sr-only'>{t('Change language')}</span>
@@ -257,13 +257,14 @@ export function PublicHeader(props: PublicHeaderProps) {
{(showLanguageSwitcher ||
showThemeSwitch ||
showNotifications) && (
<div className='bg-border/40 mx-2 h-4 w-px' />
<div className='mx-2 h-4 w-px bg-white/10' />
)}
{showLanguageSwitcher && <LanguageSwitcher />}
{showThemeSwitch && <ThemeSwitch />}
{showLanguageSwitcher && <LanguageSwitcher className='text-white/70 hover:text-white' />}
{showThemeSwitch && <ThemeSwitch className='text-white/70 hover:text-white' />}
{showNotifications && (
<NotificationPopover
className='text-white/70 hover:text-white'
open={notifications.popoverOpen}
onOpenChange={notifications.setPopoverOpen}
unreadCount={notifications.unreadCount}
@@ -277,7 +278,7 @@ export function PublicHeader(props: PublicHeaderProps) {
{showAuthButtons && (
<>
<div className='bg-border/40 mx-1 h-4 w-px' />
<div className='mx-1 h-4 w-px bg-white/10' />
{loading ? (
<Skeleton className='h-8 w-20 rounded-lg' />
) : isAuthenticated ? (
@@ -297,7 +298,7 @@ export function PublicHeader(props: PublicHeaderProps) {
{/* Mobile: compact actions + hamburger */}
<div className='flex items-center gap-2 sm:hidden'>
{showThemeSwitch && <ThemeSwitch />}
{showThemeSwitch && <ThemeSwitch className='text-white/70 hover:text-white' />}
{showAuthButtons && !loading && isAuthenticated && (
<ProfileDropdown />
)}
+2 -2
View File
@@ -29,7 +29,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
export function ThemeSwitch() {
export function ThemeSwitch({ className }: { className?: string }) {
const { t } = useTranslation()
const { theme, setTheme } = useTheme()
@@ -44,7 +44,7 @@ export function ThemeSwitch() {
return (
<DropdownMenu modal={false}>
<DropdownMenuTrigger
render={<Button variant='ghost' size='icon' className='h-9 w-9' />}
render={<Button variant='ghost' size='icon' className={cn('h-9 w-9', className)} />}
>
<Sun className='size-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90' />
<Moon className='absolute size-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0' />
+88 -45
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 } from 'lucide-react'
import { ArrowRight, Zap, Shield, Layers, BarChart3, Key, GitBranch } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { useStatus } from '@/hooks/use-status'
import { Button } from '@/components/ui/button'
@@ -27,6 +27,15 @@ interface HeroProps {
isAuthenticated?: boolean
}
const features = [
{ icon: Zap, title: 'Multi-model Routing', desc: 'Intelligent routing across providers with automatic failover and load balancing' },
{ 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()
@@ -36,68 +45,71 @@ export function Hero(props: HeroProps) {
return (
<section className='relative overflow-hidden bg-[#0C0D10]'>
{/* Subtle gradient glow */}
{/* Top gradient glow */}
<div
aria-hidden
className='pointer-events-none absolute inset-0'
style={{
background:
'radial-gradient(ellipse 80% 50% at 50% 0%, rgba(0,210,255,0.06) 0%, transparent 60%)',
'radial-gradient(ellipse 70% 40% at 50% -5%, rgba(0,210,255,0.08) 0%, transparent 70%)',
}}
/>
<div className='relative mx-auto flex min-h-[calc(100svh-3rem)] max-w-5xl flex-col items-center justify-center px-6 py-24 text-center'>
{/* Badge */}
<div className='mb-6 inline-flex items-center gap-2 rounded-full border border-[#2A2B33] bg-[#13141A] px-4 py-1.5 text-xs text-[#8B8D97]'>
<span className='inline-block size-1.5 rounded-full bg-[#00D2FF]' />
{t('Compatible with OpenAI API')}
</div>
<div className='relative mx-auto max-w-6xl px-6'>
{/* Hero area */}
<div className='flex min-h-[calc(100svh-3rem)] flex-col items-center justify-center py-20 text-center'>
{/* Badge */}
<div className='mb-6 inline-flex items-center gap-2 rounded-full border border-[#2A2B33] bg-[#13141A] px-4 py-1.5 text-xs text-[#8B8D97]'>
<span className='inline-block size-1.5 rounded-full bg-[#00D2FF]' />
{t('Compatible with OpenAI API')}
</div>
{/* Headline */}
<h1 className='max-w-3xl text-[clamp(2rem,5.5vw,3.5rem)] leading-[1.1] font-bold tracking-[-0.02em] text-white'>
{t('Unified LLM Gateway')}
</h1>
{/* Headline */}
<h1 className='max-w-3xl text-[clamp(2rem,5.5vw,3.5rem)] leading-[1.1] font-bold tracking-[-0.025em] text-white'>
{t('Unified LLM Gateway')}
</h1>
{/* Subtitle */}
<p className='mt-5 max-w-lg text-base leading-relaxed text-[#8B8D97] sm:text-lg'>
{t(
'One endpoint for all models. OpenAI-compatible, switch and go.'
)}
</p>
{/* Subtitle */}
<p className='mt-5 max-w-lg text-base leading-relaxed text-[#8B8D97] sm:text-lg'>
{t(
'One endpoint for all models. OpenAI-compatible, switch and go.'
)}
</p>
{/* CTA */}
<div className='mt-8 flex flex-wrap items-center justify-center gap-3'>
{props.isAuthenticated ? (
<Button
className='group h-11 rounded-lg bg-[#00D2FF] px-6 text-sm font-semibold text-[#0C0D10] hover:bg-[#00B8E6]'
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>
) : (
<>
{/* CTA */}
<div className='mt-8 flex flex-wrap items-center justify-center gap-3'>
{props.isAuthenticated ? (
<Button
className='group h-11 rounded-lg bg-[#00D2FF] px-6 text-sm font-semibold text-[#0C0D10] hover:bg-[#00B8E6]'
render={<Link to='/sign-up' />}
render={<Link to='/dashboard' />}
>
{t('Get Started')}
{t('Go to Dashboard')}
<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 border-[#2A2B33] bg-transparent px-6 text-sm font-medium text-[#C5C6D0] hover:border-[#3A3B45] hover:bg-[#1A1B20] hover:text-white'
render={<Link to='/pricing' />}
>
{t('View Pricing')}
</Button>
</>
)}
) : (
<>
<Button
className='group h-11 rounded-lg bg-[#00D2FF] px-6 text-sm font-semibold text-[#0C0D10] hover:bg-[#00B8E6]'
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='h-11 rounded-lg border-[#2A2B33] bg-transparent px-6 text-sm font-medium text-[#C5C6D0] hover:border-[#3A3B45] hover:bg-[#1A1B20] hover:text-white'
render={<Link to='/pricing' />}
>
{t('View Pricing')}
</Button>
</>
)}
</div>
</div>
{/* Terminal demo */}
<div className='mt-20 w-full max-w-2xl'>
<div className='overflow-hidden rounded-lg border border-[#2A2B33] bg-[#13141A] shadow-[0_8px_40px_-12px_rgba(0,0,0,0.6)]'>
{/* Terminal demo - centered, overlapping hero and features */}
<div className='-mt-10 mb-20 flex justify-center pb-8'>
<div className='w-full max-w-2xl overflow-hidden rounded-lg border border-[#2A2B33] bg-[#13141A] shadow-[0_8px_40px_-12px_rgba(0,0,0,0.6)]'>
{/* Terminal header */}
<div className='flex items-center gap-2 border-b border-[#2A2B33] px-4 py-3'>
<div className='flex gap-1.5'>
@@ -149,6 +161,37 @@ export function Hero(props: HeroProps) {
</div>
</div>
</div>
{/* Features grid */}
<div className='pb-24'>
<div className='mb-12 text-center'>
<h2 className='text-xl font-semibold tracking-tight text-white sm:text-2xl'>
{t('Full-featured gateway')}
</h2>
<p className='mt-2 text-sm text-[#8B8D97]'>
{t('Everything you need to manage, route, and monitor LLM API traffic')}
</p>
</div>
<div className='grid gap-4 sm:grid-cols-2 lg:grid-cols-3'>
{features.map((feature) => (
<div
key={feature.title}
className='group rounded-lg border border-[#2A2B33] bg-[#13141A] p-5 transition-colors duration-200 hover:border-[#3A3B45]'
>
<div className='mb-3 flex size-9 items-center justify-center rounded-md bg-[#00D2FF]/10'>
<feature.icon className='size-[18px] text-[#00D2FF]' />
</div>
<h3 className='text-sm font-semibold text-[#C5C6D0]'>
{t(feature.title)}
</h3>
<p className='mt-1.5 text-[13px] leading-relaxed text-[#5C5D66]'>
{t(feature.desc)}
</p>
</div>
))}
</div>
</div>
</div>
</section>
)
+13
View File
@@ -940,6 +940,19 @@
"Full-featured gateway": "Full-featured gateway",
"Key management": "Key management",
"Multi-model routing": "Multi-model routing",
"Multi-model Routing": "Multi-model Routing",
"Intelligent routing across providers with automatic failover and load balancing": "Intelligent routing across providers with automatic failover and load balancing",
"Key Management": "Key Management",
"Centralized API key lifecycle management with usage tracking and rotation": "Centralized API key lifecycle management with usage tracking and rotation",
"Access Control": "Access Control",
"Fine-grained permissions, rate limiting, and token-level access policies": "Fine-grained permissions, rate limiting, and token-level access policies",
"Real-time Monitoring": "Real-time Monitoring",
"Live dashboards with request latency, token usage, and cost analytics": "Live dashboards with request latency, token usage, and cost analytics",
"OpenAI Compatible": "OpenAI Compatible",
"Drop-in replacement for OpenAI API — no code changes required": "Drop-in replacement for OpenAI API — no code changes required",
"Model Mapping": "Model Mapping",
"Map model names, override parameters, and customize per-channel behavior": "Map model names, override parameters, and customize per-channel behavior",
"Everything you need to manage, route, and monitor LLM API traffic": "Everything you need to manage, route, and monitor LLM API traffic",
"No more re-entering API keys. Everything is saved, so you connect instantly.": "No more re-entering API keys. Everything is saved, so you connect instantly.",
"Real-time monitoring": "Real-time monitoring",
"Ready to get started?": "Ready to get started?",
+13
View File
@@ -940,6 +940,19 @@
"Full-featured gateway": "全功能网关",
"Key management": "密钥管理",
"Multi-model routing": "多模型路由",
"Multi-model Routing": "多模型路由",
"Intelligent routing across providers with automatic failover and load balancing": "跨供应商智能路由,自动故障转移与负载均衡",
"Key Management": "密钥管理",
"Centralized API key lifecycle management with usage tracking and rotation": "集中管理 API 密钥生命周期,追踪用量与轮换",
"Access Control": "访问控制",
"Fine-grained permissions, rate limiting, and token-level access policies": "细粒度权限、速率限制与令牌级访问策略",
"Real-time Monitoring": "实时监控",
"Live dashboards with request latency, token usage, and cost analytics": "实时仪表盘,监控请求延迟、令牌用量与成本分析",
"OpenAI Compatible": "兼容 OpenAI",
"Drop-in replacement for OpenAI API — no code changes required": "直接替换 OpenAI API,无需修改代码",
"Model Mapping": "模型映射",
"Map model names, override parameters, and customize per-channel behavior": "映射模型名称、覆盖参数、自定义渠道行为",
"Everything you need to manage, route, and monitor LLM API traffic": "管理、路由和监控大模型 API 流量所需的一切",
"No more re-entering API keys. Everything is saved, so you connect instantly.": "无需重复输入 API 密钥,一切已保存,即刻连接。",
"Real-time monitoring": "实时监控",
"Ready to get started?": "准备开始了吗?",