fix: correct primary colors, redesign hero, fix header text colors
Docker Build / Build and Push Docker Image (push) Successful in 4m7s
Docker Build / Build and Push Docker Image (push) Successful in 4m7s
This commit is contained in:
+17
-51
@@ -154,16 +154,12 @@ export function Footer(props: FooterProps) {
|
||||
systemName,
|
||||
logo: systemLogo,
|
||||
footerHtml,
|
||||
demoSiteEnabled,
|
||||
} = useSystemConfig()
|
||||
|
||||
const displayLogo = systemLogo || props.logo || '/logo.png'
|
||||
const displayName = systemName || props.name || 'New API'
|
||||
const isDemoSiteMode = Boolean(demoSiteEnabled)
|
||||
const currentYear = new Date().getFullYear()
|
||||
|
||||
const displayColumns = props.columns ?? []
|
||||
|
||||
if (footerHtml) {
|
||||
return (
|
||||
<footer
|
||||
@@ -192,56 +188,26 @@ export function Footer(props: FooterProps) {
|
||||
<footer
|
||||
className={cn('border-[#2A2B33] bg-[#0C0D10] border-t', props.className)}
|
||||
>
|
||||
<div className='mx-auto max-w-6xl px-6 py-10'>
|
||||
<div className='flex flex-col justify-between gap-8 md:flex-row md:gap-16'>
|
||||
{/* Brand column */}
|
||||
<div className='shrink-0'>
|
||||
<Link to='/' className='group flex items-center gap-2.5'>
|
||||
<img
|
||||
src={displayLogo}
|
||||
alt={displayName}
|
||||
className='size-6 rounded object-contain'
|
||||
/>
|
||||
<span className='text-sm font-semibold tracking-tight text-[#C5C6D0]'>
|
||||
{displayName}
|
||||
</span>
|
||||
</Link>
|
||||
<p className='mt-2.5 max-w-[200px] text-xs leading-relaxed text-[#5C5D66]'>
|
||||
{t('Powerful API Management Platform')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Links columns */}
|
||||
{isDemoSiteMode && displayColumns.length > 0 && (
|
||||
<div className='grid grid-cols-3 gap-8 md:gap-16'>
|
||||
{displayColumns.map((column, index) => (
|
||||
<div key={index}>
|
||||
<p className='mb-2.5 text-[10px] font-medium tracking-widest uppercase text-[#5C5D66]'>
|
||||
{t(column.title)}
|
||||
</p>
|
||||
<ul className='space-y-2'>
|
||||
{column.links.map((link, linkIndex) => (
|
||||
<li key={linkIndex}>
|
||||
<FooterLinkItem link={link} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Copyright row */}
|
||||
<div className='border-[#2A2B33] mt-8 flex flex-col items-center justify-between gap-x-3 gap-y-2 border-t pt-5 sm:flex-row'>
|
||||
<div className='text-[#5C5D66] flex flex-wrap items-center justify-center gap-x-2 gap-y-1 text-xs sm:justify-start'>
|
||||
<span>
|
||||
© {currentYear} {displayName}.{' '}
|
||||
{props.copyright ?? t('footer.defaultCopyright')}
|
||||
<div className='mx-auto max-w-5xl px-6 py-8'>
|
||||
<div className='flex flex-col items-center justify-between gap-4 sm:flex-row'>
|
||||
{/* Brand */}
|
||||
<Link to='/' className='group flex items-center gap-2'>
|
||||
<img
|
||||
src={displayLogo}
|
||||
alt={displayName}
|
||||
className='size-5 rounded object-contain'
|
||||
/>
|
||||
<span className='text-xs font-semibold tracking-tight text-[#C5C6D0]'>
|
||||
{displayName}
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
{/* Links + Copyright */}
|
||||
<div className='text-[#5C5D66] flex flex-wrap items-center justify-center gap-x-2 gap-y-1 text-xs'>
|
||||
<span>© {currentYear} {displayName}</span>
|
||||
<LegalLinks leadingSeparator />
|
||||
<ProjectAttribution currentYear={currentYear} inline />
|
||||
</div>
|
||||
<ProjectAttribution currentYear={currentYear} />
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -207,7 +207,7 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<span className='text-sm font-semibold tracking-tight'>
|
||||
<span className='text-sm font-semibold tracking-tight text-white'>
|
||||
{loading ? <Skeleton className='h-4 w-16' /> : displaySiteName}
|
||||
</span>
|
||||
</Link>
|
||||
@@ -227,7 +227,7 @@ export function PublicHeader(props: PublicHeaderProps) {
|
||||
tabIndex={link.disabled ? -1 : undefined}
|
||||
onClick={(event) => handleNavLinkClick(event, link)}
|
||||
className={cn(
|
||||
'text-muted-foreground hover:text-foreground rounded-lg px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
|
||||
'text-[#8B8D97] hover:text-white rounded-lg px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
|
||||
link.disabled && 'pointer-events-none opacity-50'
|
||||
)}
|
||||
>
|
||||
@@ -242,12 +242,12 @@ 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',
|
||||
isActive
|
||||
? 'text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground',
|
||||
link.disabled && 'pointer-events-none opacity-50'
|
||||
)}
|
||||
'rounded-lg px-3 py-1.5 text-[13px] font-medium transition-colors duration-200',
|
||||
isActive
|
||||
? 'text-white'
|
||||
: 'text-[#8B8D97] hover:text-white',
|
||||
link.disabled && 'pointer-events-none opacity-50'
|
||||
)}
|
||||
>
|
||||
{t(link.title)}
|
||||
</Link>
|
||||
|
||||
+46
-26
@@ -35,15 +35,31 @@ export function Hero(props: HeroProps) {
|
||||
`${window.location.origin}`
|
||||
|
||||
return (
|
||||
<section className='bg-[#0C0D10]'>
|
||||
<div className='mx-auto flex min-h-[calc(100svh-3rem)] max-w-6xl flex-col items-center justify-center px-6 py-20 text-center'>
|
||||
<section className='relative overflow-hidden bg-[#0C0D10]'>
|
||||
{/* Subtle 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%)',
|
||||
}}
|
||||
/>
|
||||
|
||||
<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>
|
||||
|
||||
{/* Headline */}
|
||||
<h1 className='max-w-2xl text-[clamp(1.75rem,4.5vw,3rem)] leading-[1.15] font-bold tracking-tight text-white'>
|
||||
<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>
|
||||
|
||||
{/* Subtitle */}
|
||||
<p className='mt-4 max-w-md text-base leading-relaxed text-[#8B8D97]'>
|
||||
<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.'
|
||||
)}
|
||||
@@ -80,38 +96,42 @@ export function Hero(props: HeroProps) {
|
||||
</div>
|
||||
|
||||
{/* Terminal demo */}
|
||||
<div className='mt-16 w-full max-w-3xl'>
|
||||
<div className='overflow-hidden rounded-xl border border-[#2A2B33] bg-[#13141A] shadow-2xl shadow-black/50'>
|
||||
<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 header */}
|
||||
<div className='flex items-center border-b border-[#2A2B33] px-4 py-2.5'>
|
||||
<div className='flex items-center gap-2 border-b border-[#2A2B33] px-4 py-3'>
|
||||
<div className='flex gap-1.5'>
|
||||
<div className='size-2.5 rounded-full bg-[#FF5F57]' />
|
||||
<div className='size-2.5 rounded-full bg-[#FEBC2E]' />
|
||||
<div className='size-2.5 rounded-full bg-[#28C840]' />
|
||||
<div className='size-[9px] rounded-full bg-[#FF5F57]' />
|
||||
<div className='size-[9px] rounded-full bg-[#FEBC2E]' />
|
||||
<div className='size-[9px] rounded-full bg-[#28C840]' />
|
||||
</div>
|
||||
<span className='ml-3 text-[11px] text-[#5C5D66]'>
|
||||
ModelsToken Gateway
|
||||
<span className='ml-1 text-[11px] font-medium text-[#5C5D66]'>
|
||||
bash
|
||||
</span>
|
||||
</div>
|
||||
{/* Terminal content */}
|
||||
<div className='p-5 font-mono text-[13px] leading-6'>
|
||||
<div className='p-5 font-mono text-[13px] leading-[1.8]'>
|
||||
<div>
|
||||
<span className='text-[#28C840]'>$</span>{' '}
|
||||
<span className='select-none text-[#5C5D66]'>$</span>{' '}
|
||||
<span className='text-[#C5C6D0]'>curl</span>{' '}
|
||||
<span className='text-[#00D2FF]'>{serverAddress}/v1/chat/completions</span>
|
||||
<span className='text-[#00D2FF]'>{serverAddress}/v1/chat/completions</span>{' '}
|
||||
<span className='text-[#5C5D66]'>\</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className='pl-4'>
|
||||
<span className='text-[#5C5D66]'>-H</span>{' '}
|
||||
<span className='text-[#FEBC2E]'>"Authorization: Bearer sk-..."</span>
|
||||
<span className='text-[#FEBC2E]'>"Authorization: Bearer sk-..."</span>{' '}
|
||||
<span className='text-[#5C5D66]'>\</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className='pl-4'>
|
||||
<span className='text-[#5C5D66]'>-d</span>{' '}
|
||||
<span className='text-[#FEBC2E]'>{'\'{ "model": "gpt-4o", "messages": [...] }\' '}</span>
|
||||
<span className='text-[#FEBC2E]'>{'\'{ "model": "gpt-4o", "messages": [...] }\''}</span>
|
||||
</div>
|
||||
<div className='mt-3 border-t border-[#2A2B33] pt-3'>
|
||||
<span className='text-[#5C5D66]'># 200 OK</span>
|
||||
<div className='mt-4 flex items-center gap-2 border-t border-[#2A2B33] pt-4'>
|
||||
<span className='inline-block size-1.5 rounded-full bg-[#28C840]' />
|
||||
<span className='text-[11px] font-medium text-[#5C5D66]'>200 OK</span>
|
||||
<span className='text-[11px] text-[#5C5D66]'>-- 312ms</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className='mt-2'>
|
||||
<span className='text-[#5C5D66]'>{'{'}</span>{' '}
|
||||
<span className='text-[#00D2FF]'>"model"</span>
|
||||
<span className='text-[#5C5D66]'>:</span>{' '}
|
||||
@@ -119,12 +139,12 @@ export function Hero(props: HeroProps) {
|
||||
<span className='text-[#5C5D66]'>,</span>{' '}
|
||||
<span className='text-[#00D2FF]'>"usage"</span>
|
||||
<span className='text-[#5C5D66]'>:</span>{' '}
|
||||
<span className='text-[#5C5D66]'>{'{'}...{'}'}</span>{' '}
|
||||
<span className='text-[#5C5D66]'>{'{'}...{'}'}</span>
|
||||
<span className='text-[#5C5D66]'>{'}'}</span>
|
||||
</div>
|
||||
<div className='mt-2'>
|
||||
<span className='text-[#28C840]'>$</span>
|
||||
<span className='animate-pulse text-[#8B8D97]'> _</span>
|
||||
<div className='mt-3'>
|
||||
<span className='select-none text-[#5C5D66]'>$</span>
|
||||
<span className='ml-1 inline-block h-3.5 w-[7px] animate-pulse bg-[#C5C6D0] align-middle' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Vendored
+1
@@ -935,6 +935,7 @@
|
||||
"Providers": "Providers",
|
||||
"Supported Providers": "Supported Providers",
|
||||
"Compatible with": "Compatible with",
|
||||
"Compatible with OpenAI API": "Compatible with OpenAI API",
|
||||
"Connect with one click": "Connect with one click",
|
||||
"Full-featured gateway": "Full-featured gateway",
|
||||
"Key management": "Key management",
|
||||
|
||||
Vendored
+1
@@ -935,6 +935,7 @@
|
||||
"Providers": "供应商",
|
||||
"Supported Providers": "支持的供应商",
|
||||
"Compatible with": "兼容",
|
||||
"Compatible with OpenAI API": "兼容 OpenAI API",
|
||||
"Connect with one click": "一键连接",
|
||||
"Full-featured gateway": "全功能网关",
|
||||
"Key management": "密钥管理",
|
||||
|
||||
Vendored
+33
-33
@@ -91,7 +91,7 @@ For commercial licensing, please contact admin@modelstoken.com
|
||||
}
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--radius: 0.5rem;
|
||||
--app-header-height: 3rem;
|
||||
/* Static build-channel fallback consumed when JS hasn't booted yet. */
|
||||
--app-rev: '2k6e8r7p';
|
||||
@@ -100,65 +100,65 @@ For commercial licensing, please contact admin@modelstoken.com
|
||||
* in theme-presets.css. Default mirrors `--font-sans` so behavior is
|
||||
* identical when no font preference is set. */
|
||||
--font-body: var(--font-sans);
|
||||
/* Termius-inspired light mode: clean whites, subtle borders, cyan accent */
|
||||
--background: oklch(0.985 0.001 270);
|
||||
--foreground: oklch(0.15 0.005 270);
|
||||
/* Termius-inspired light mode: clean whites, deep primary, cyan accents */
|
||||
--background: oklch(0.99 0 0);
|
||||
--foreground: oklch(0.145 0.005 270);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.15 0.005 270);
|
||||
--card-foreground: oklch(0.145 0.005 270);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.15 0.005 270);
|
||||
--primary: oklch(0.65 0.17 210);
|
||||
--primary-foreground: oklch(1 0 0);
|
||||
--secondary: oklch(0.955 0.003 270);
|
||||
--secondary-foreground: oklch(0.20 0.005 270);
|
||||
--muted: oklch(0.955 0.003 270);
|
||||
--muted-foreground: oklch(0.50 0.01 270);
|
||||
--accent: oklch(0.955 0.003 270);
|
||||
--accent-foreground: oklch(0.15 0.005 270);
|
||||
--popover-foreground: oklch(0.145 0.005 270);
|
||||
--primary: oklch(0.205 0.02 260);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.96 0.005 260);
|
||||
--secondary-foreground: oklch(0.205 0.005 270);
|
||||
--muted: oklch(0.96 0.005 260);
|
||||
--muted-foreground: oklch(0.50 0.01 260);
|
||||
--accent: oklch(0.96 0.005 260);
|
||||
--accent-foreground: oklch(0.145 0.005 270);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--destructive-foreground: oklch(0.985 0 0);
|
||||
--success: oklch(0.596 0.145 163.225);
|
||||
--success-foreground: oklch(0.985 0 0);
|
||||
--warning: oklch(0.681 0.162 75.834);
|
||||
--warning-foreground: oklch(0.15 0.005 270);
|
||||
--warning-foreground: oklch(0.145 0.005 270);
|
||||
--info: oklch(0.588 0.158 241.966);
|
||||
--info-foreground: oklch(0.985 0 0);
|
||||
--neutral: oklch(0.55 0.01 270);
|
||||
--neutral: oklch(0.55 0.01 260);
|
||||
--neutral-foreground: oklch(0.985 0 0);
|
||||
--border: oklch(0.91 0.003 270);
|
||||
--input: oklch(0.91 0.003 270);
|
||||
--border: oklch(0.92 0.003 260);
|
||||
--input: oklch(0.92 0.003 260);
|
||||
--ring: oklch(0.65 0.17 210);
|
||||
--chart-1: oklch(0.65 0.17 210);
|
||||
--chart-2: oklch(0.596 0.145 163.225);
|
||||
--chart-3: oklch(0.681 0.162 75.834);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.975 0.002 270);
|
||||
--sidebar-foreground: oklch(0.15 0.005 270);
|
||||
--sidebar: oklch(0.975 0.003 260);
|
||||
--sidebar-foreground: oklch(0.145 0.005 270);
|
||||
--sidebar-primary: oklch(0.65 0.17 210);
|
||||
--sidebar-primary-foreground: oklch(1 0 0);
|
||||
--sidebar-accent: oklch(0.935 0.003 270);
|
||||
--sidebar-accent-foreground: oklch(0.15 0.005 270);
|
||||
--sidebar-border: oklch(0.91 0.003 270);
|
||||
--sidebar-accent: oklch(0.94 0.005 260);
|
||||
--sidebar-accent-foreground: oklch(0.145 0.005 270);
|
||||
--sidebar-border: oklch(0.92 0.003 260);
|
||||
--sidebar-ring: oklch(0.65 0.17 210);
|
||||
--skeleton-base: oklch(0.955 0.003 270);
|
||||
--skeleton-highlight: oklch(0.985 0.001 270);
|
||||
--skeleton-base: oklch(0.96 0.005 260);
|
||||
--skeleton-highlight: oklch(0.99 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
/* Termius-inspired dark mode: deep charcoal, cyan accent */
|
||||
/* Termius-inspired dark mode: deep charcoal, muted cyan accent */
|
||||
--background: oklch(0.13 0.005 270);
|
||||
--foreground: oklch(0.93 0.005 270);
|
||||
--card: oklch(0.17 0.005 270);
|
||||
--card-foreground: oklch(0.93 0.005 270);
|
||||
--popover: oklch(0.17 0.005 270);
|
||||
--popover-foreground: oklch(0.93 0.005 270);
|
||||
--primary: oklch(0.78 0.15 210);
|
||||
--primary: oklch(0.72 0.14 210);
|
||||
--primary-foreground: oklch(0.13 0.005 270);
|
||||
--secondary: oklch(0.22 0.005 270);
|
||||
--secondary-foreground: oklch(0.88 0.005 270);
|
||||
--muted: oklch(0.20 0.005 270);
|
||||
--muted-foreground: oklch(0.62 0.01 270);
|
||||
--muted-foreground: oklch(0.58 0.01 270);
|
||||
--accent: oklch(0.25 0.005 270);
|
||||
--accent-foreground: oklch(0.93 0.005 270);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
@@ -169,24 +169,24 @@ For commercial licensing, please contact admin@modelstoken.com
|
||||
--warning-foreground: oklch(0.13 0.005 270);
|
||||
--info: oklch(0.68 0.17 237.323);
|
||||
--info-foreground: oklch(0.13 0.005 270);
|
||||
--neutral: oklch(0.62 0.01 270);
|
||||
--neutral: oklch(0.58 0.01 270);
|
||||
--neutral-foreground: oklch(0.13 0.005 270);
|
||||
--border: oklch(0.25 0.005 270);
|
||||
--input: oklch(0.25 0.005 270);
|
||||
--ring: oklch(0.78 0.15 210);
|
||||
--chart-1: oklch(0.78 0.15 210);
|
||||
--ring: oklch(0.72 0.14 210);
|
||||
--chart-1: oklch(0.72 0.14 210);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.11 0.005 270);
|
||||
--sidebar-foreground: oklch(0.88 0.005 270);
|
||||
--sidebar-primary: oklch(0.78 0.15 210);
|
||||
--sidebar-primary: oklch(0.72 0.14 210);
|
||||
--sidebar-primary-foreground: oklch(0.13 0.005 270);
|
||||
--sidebar-accent: oklch(0.20 0.005 270);
|
||||
--sidebar-accent-foreground: oklch(0.88 0.005 270);
|
||||
--sidebar-border: oklch(0.25 0.005 270);
|
||||
--sidebar-ring: oklch(0.78 0.15 210);
|
||||
--sidebar-ring: oklch(0.72 0.14 210);
|
||||
--skeleton-base: oklch(0.20 0.005 270);
|
||||
--skeleton-highlight: oklch(0.28 0.005 270);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user