TuringToken/web/src/pages/Home/index.jsx

775 lines
32 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Copy, Check, Code, Terminal, FileCode, Box, Grid3X3, Play, Trophy, PieChart, BookOpen, Search, Sun, Moon, Monitor, Languages } from 'lucide-react';
/* ==================================================================
TokenDance-style Home Page
Design: black/white minimal with serif headings
================================================================== */
/* ─── Hero Marquee Carousel Texts ────────────────────────── */
const CAROUSEL_TEXTS = [
'OpenAI 协议',
'Claude 协议',
'Gemini 协议',
'智能路由',
'统一计费',
'模型丰富',
'安全可靠',
'开箱即用',
];
/* ─── Hero Section ──────────────────────────────────────────── */
/* --- Helper: Theme Toggle --- */
function HomeThemeToggle() {
const { t } = useTranslation();
const [open, setOpen] = useState(false);
const ref = useRef(null);
useEffect(() => {
if (!open) return;
const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener("mousedown", handler);
return () => document.removeEventListener("mousedown", handler);
}, [open]);
const theme = localStorage.getItem("theme-mode") || "auto";
const applyTheme = useCallback((val) => {
const actualTheme = val === 'auto'
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: val;
if (actualTheme === 'dark') {
document.body.setAttribute('theme-mode', 'dark');
document.documentElement.classList.add('dark');
} else {
document.body.removeAttribute('theme-mode');
document.documentElement.classList.remove('dark');
}
}, []);
const setTheme = (val) => {
localStorage.setItem("theme-mode", val);
applyTheme(val);
setOpen(false);
};
useEffect(() => {
applyTheme(theme);
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleChange = () => {
const stored = localStorage.getItem("theme-mode") || "auto";
applyTheme(stored);
};
mediaQuery.addEventListener('change', handleChange);
return () => mediaQuery.removeEventListener('change', handleChange);
}, [applyTheme]);
const icons = { light: <Sun size={16} />, dark: <Moon size={16} />, auto: <Monitor size={16} /> };
return (
<div className="relative" ref={ref}>
<button type="button" onClick={() => setOpen(!open)} className="inline-flex items-center justify-center p-2 rounded-md text-white/60 hover:text-white hover:bg-white/10 transition-colors">
{icons[theme] || icons.auto}
</button>
{open && (
<div className="absolute right-0 top-full mt-1 z-50 min-w-[10rem] rounded-lg border border-white/10 bg-neutral-900 p-1 shadow-md">
{[
{ key: "light", icon: <Sun size={16} />, label: t("浅色模式") },
{ key: "dark", icon: <Moon size={16} />, label: t("深色模式") },
{ key: "auto", icon: <Monitor size={16} />, label: t("自动模式") },
].map((opt) => (
<button key={opt.key} type="button" onClick={() => setTheme(opt.key)} className={`flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-sm transition-colors ${theme === opt.key ? "bg-white/10 text-white font-medium" : "text-white/50 hover:bg-white/10 hover:text-white"}`}>
{opt.icon}<span>{opt.label}</span>
{theme === opt.key && <span className="ml-auto flex h-3.5 w-3.5 items-center justify-center"><span className="h-1.5 w-1.5 rounded-full bg-white"></span></span>}
</button>
))}
</div>
)}
</div>
);
}
/* --- Helper: Language Selector --- */
function HomeLanguageSelector() {
const { t, i18n } = useTranslation();
const [open, setOpen] = useState(false);
const ref = useRef(null);
useEffect(() => {
if (!open) return;
const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener("mousedown", handler);
return () => document.removeEventListener("mousedown", handler);
}, [open]);
const current = (i18n.language || "").startsWith("zh") ? "zh-CN" : "en";
const switchLang = (code) => { i18n.changeLanguage(code); setOpen(false); };
const langLabels = { "zh-CN": "简体中文", "en": "English" };
return (
<div className="relative" ref={ref}>
<button type="button" onClick={() => setOpen(!open)} className="inline-flex items-center gap-1 rounded-md px-2 py-1.5 text-sm text-white/60 hover:text-white hover:bg-white/10 transition-colors">
<Languages size={16} /><span className="hidden sm:inline text-xs">{langLabels[current] || current}</span>
</button>
{open && (
<div className="absolute right-0 top-full mt-1 z-50 min-w-[8rem] rounded-lg border border-white/10 bg-neutral-900 p-1 shadow-md">
{Object.entries(langLabels).map(([code, label]) => (
<button key={code} type="button" onClick={() => switchLang(code)} className={`flex w-full items-center rounded-md px-2.5 py-1.5 text-sm transition-colors ${current === code ? "bg-white/10 text-white font-medium" : "text-white/50 hover:bg-white/10 hover:text-white"}`}>
{label}
{current === code && <span className="ml-auto flex h-3.5 w-3.5 items-center justify-center"><span className="h-1.5 w-1.5 rounded-full bg-white"></span></span>}
</button>
))}
</div>
)}
</div>
);
}
function HeroSection() {
const { t } = useTranslation();
const [copied, setCopied] = useState(false);
const navigate = useNavigate();
const copyCmd = useCallback(() => {
navigator.clipboard.writeText("https://tokendance.space");
setCopied(true);
setTimeout(() => setCopied(false), 1500);
}, []);
return (
<section className="relative bg-black text-white overflow-hidden">
<div className="absolute inset-0 pointer-events-none opacity-[0.04]" style={{ backgroundImage: "radial-gradient(circle, white 1px, transparent 1px)", backgroundSize: "24px 24px" }} />
<div className="fixed top-0 z-50 w-full border-b border-white/10 bg-black">
<div className="flex h-14 items-center px-4 lg:px-6 max-w-[1400px] mx-auto">
<Link to="/" className="flex items-center gap-2 flex-shrink-0">
<img src="/logo.jpg" alt="logo" className="w-7 h-7 rounded-full object-cover" />
<span className="font-heading text-lg font-bold tracking-tight text-white">TuringToken</span>
<span className="text-[10px] font-mono px-1.5 py-0.5 rounded border border-white/20 text-white/40">BETA</span>
</Link>
<div className="hidden md:block flex-1" />
<nav className="hidden md:flex items-center gap-0.5">
{[
{ key: "model", label: "模型", icon: <Box size={16} />, to: "/pricing" },
{ key: "benchmark", label: "评测", icon: <Trophy size={16} />, to: "/benchmarks" },
{ key: "docs", label: "文档", icon: <BookOpen size={16} />, to: "https://docs.new-api.cc", external: true },
{ key: "console", label: "控制台", icon: <Grid3X3 size={16} />, to: "/console" },
].map((item) => {
const linkClass = "inline-flex items-center gap-1.5 px-3 py-1.5 text-[14px] rounded-md transition-colors duration-150 text-white/60 hover:text-white";
if (item.external) {
return <a key={item.key} href={item.to} target="_blank" rel="noopener noreferrer" className={linkClass}>{item.icon}<span className="hidden sm:inline">{t(item.label)}</span></a>;
}
return <Link key={item.key} to={item.to} className={linkClass}>{item.icon}<span className="hidden sm:inline">{t(item.label)}</span></Link>;
})}
</nav>
<div className="flex items-center gap-1">
<HomeThemeToggle />
<HomeLanguageSelector />
<Link to="/login" className="inline-flex items-center bg-white text-black px-4 py-1.5 text-sm font-mono font-medium uppercase tracking-widest hover:bg-transparent hover:text-white border border-white transition-colors duration-200 ml-2">
{t("登录")}
</Link>
</div>
</div>
</div>
<div className="relative flex-1 grid md:grid-cols-2 gap-8 px-6 md:px-12 pt-[88px] pb-12 items-center max-w-[1400px] mx-auto w-full" style={{ minHeight: "90vh" }}>
<div className="home-fade-in">
<p className="font-mono text-sm text-white/30 mb-6 tracking-wide font-body">
{"// unified model API gateway"}
</p>
<div className="w-[60px] h-[4px] bg-white mb-6" />
<h1 className="font-heading text-3xl sm:text-5xl md:text-6xl lg:text-7xl font-bold leading-[1.1] mb-6">
{t("更快、更稳、更省")}<br />{t("Token服务平台")}
</h1>
<p className="font-body text-white/50 text-base md:text-lg leading-relaxed mb-10">
{t("兼容 OpenAI / Claude / Gemini 等协议,覆盖文本、图像、视频、语音,智能路由,统一计费。")}
</p>
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-3 sm:gap-4 font-body">
<Link to="/register" className="inline-flex items-center bg-white text-black px-6 py-3 text-sm font-mono font-medium uppercase tracking-widest hover:bg-transparent hover:text-white border border-white transition-colors duration-200">
{t("开始使用")}
</Link>
<Link to="/pricing" className="inline-flex items-center text-white/60 px-6 py-3 text-sm font-mono font-medium uppercase tracking-widest hover:text-white transition-colors duration-200">
{t("查看价格")} &rarr;
</Link>
</div>
</div>
<div className="home-fade-in-delay-1 font-body">
<div className="bg-neutral-900 border border-white/10 rounded-none overflow-hidden">
<div className="flex items-center justify-between px-4 py-2.5 border-b border-white/10 bg-neutral-800/50">
<div className="flex gap-1.5">
<span className="w-3 h-3 rounded-full bg-red-500/60" />
<span className="w-3 h-3 rounded-full bg-yellow-500/60" />
<span className="w-3 h-3 rounded-full bg-green-500/60" />
</div>
<span className="font-mono text-[10px] text-white/20 tracking-wide">terminal</span>
<button type="button" onClick={copyCmd} className="text-white/30 hover:text-white/70 transition-colors" aria-label="Copy">
{copied ? <Check size={14} /> : <Copy size={14} />}
</button>
</div>
<pre className="p-5 text-sm font-mono leading-relaxed text-white/80 overflow-x-auto">
<code>{`curl https://api.tokendance.space/v1/chat/completions \\
-H "Authorization: Bearer \$YOUR_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Hello"}]
}'`}</code>
</pre>
</div>
</div>
</div>
{/* Hero brand wordmark — TURINGTOKEN */}
<div className="w-full overflow-hidden px-6 pb-2">
<div
className="inline-flex whitespace-nowrap origin-left font-mono font-bold uppercase leading-[0.85] select-none tracking-tight"
style={{ fontSize: 'clamp(4rem, 20vw, 12rem)', transform: 'scaleX(1.04)' }}
>
<span className="text-white">Turing</span>
<span className="text-white/30" style={{ textShadow: '0 0 30px rgba(255,255,255,0.15), 4px 4px 12px rgba(0,0,0,0.5)' }}>Token</span>
</div>
</div>
{/* Feature marquee strip — 轮播: OpenAI 协议 / Claude 协议 / 智能路由 ... */}
<div className="w-full border-t border-white/10 py-5 px-6 overflow-hidden">
<div className="home-marquee flex gap-12 items-center">
{[...CAROUSEL_TEXTS, ...CAROUSEL_TEXTS].map((text, i) => (
<span
key={i}
className="font-mono text-sm text-white/40 whitespace-nowrap tracking-wider"
>
{text}
</span>
))}
</div>
</div>
</section>
);
}const FEATURES = [
{
titleKey: '多协议兼容',
descKey:
'原生支持 OpenAI、Claude、Gemini 文本协议,覆盖图像 / 视频 / 文本转语音生成。无需修改代码,切换 Base URL 即可接入。',
code: 'baseURL: "api.tokendance.space"',
},
{
titleKey: '智能路由',
descKey:
'根据模型名称自动路由至对应供应商。一个入口,无需关心底层调度。',
code: 'route(model) → provider',
},
{
titleKey: '统一计费',
descKey:
'跨供应商统一 Token 消耗统计与账单。告别多平台分别充值的混乱。',
code: 'billing.unified()',
},
{
titleKey: '容错降级',
descKey:
'同一模型支持多供应商端点自动切换;单次请求可指定多个候选模型,逐级降级,保障服务持续可用。',
code: 'fallback: model[] → provider[]',
},
{
titleKey: '模型丰富',
descKey:
'接入 MiniMax、通义千问、Kimi、智谱、DeepSeek 等国内头部模型。持续扩展中。',
code: 'models.list() → 80+',
},
{
titleKey: '开箱即用',
descKey:
'一键登录,分钟级接入。兼容现有 SDK零迁移成本。',
code: 'import OpenAI from "openai"',
},
];
function FeaturesSection() {
const { t } = useTranslation();
return (
<section id="features" className="py-24 px-6">
<div className="max-w-6xl mx-auto">
<p className="font-mono text-xs text-muted-foreground tracking-widest uppercase mb-3">
{/* features */}
</p>
<h2 className="font-heading text-3xl font-bold md:text-4xl tracking-tight mb-16 text-foreground">
{t('为接入 AI 模型的开发者而造。')}
</h2>
<div className="grid sm:grid-cols-2 lg:grid-cols-3">
{FEATURES.map((f, i) => (
<div
key={i}
className="group p-8 flex flex-col transition-all duration-200 hover:bg-foreground hover:text-background border-b sm:border-r border-border"
>
<code className="inline-block text-xs mb-4 font-mono border border-foreground/10 px-2 py-1 w-fit group-hover:border-background/30 group-hover:text-background transition-all duration-200">
{f.code}
</code>
<h3 className="font-heading text-lg font-bold mb-2 text-foreground group-hover:text-background transition-all duration-200">
{t(f.titleKey)}
</h3>
<p className="text-sm text-muted-foreground leading-relaxed group-hover:text-background/70 transition-all duration-200">
{t(f.descKey)}
</p>
</div>
))}
</div>
</div>
</section>
);
}
/* ─── How It Works Section ──────────────────────────────────── */
const STEPS = [
{
step: '01',
titleKey: '注册账号',
descKey: '通过邮箱一键注册,即刻开始使用。',
link: '/register',
},
{
step: '02',
titleKey: '创建 API Key',
descKey: '在控制台创建密钥,支持多 Key 管理与权限控制。',
link: '/console/token',
},
{
step: '03',
titleKey: '发起请求',
descKey: '使用你熟悉的 SDK 调用任意模型,完全兼容原生协议。',
link: '/pricing',
},
];
function HowItWorksSection() {
const { t } = useTranslation();
return (
<section
id="how-it-works"
className="border-y-4 border-border"
>
<div className="max-w-5xl mx-auto">
<div className="px-6 md:px-12 pt-24 pb-12">
<div className="font-mono text-xs text-muted-foreground tracking-widest uppercase mb-3">
{'// how it works'}
</div>
<h2 className="font-heading text-3xl font-bold md:text-4xl tracking-tight text-foreground">
{t('三步接入,分钟级上线')}
</h2>
</div>
{STEPS.map((s, i) => (
<Link
key={i}
to={s.link}
className={`group block border-t border-border ${
i === STEPS.length - 1 ? 'border-b border-border' : ''
} hover:bg-foreground transition-all duration-200`}
>
<div className="grid md:grid-cols-[120px_1fr_auto] gap-0 items-center">
<div className="px-6 md:px-12 py-8 md:border-r border-border group-hover:border-background/10 flex items-baseline transition-all duration-200">
<span className="font-mono text-5xl md:text-6xl font-bold text-muted-foreground/30 group-hover:text-background leading-none transition-all duration-200">
{s.step}
</span>
</div>
<div className="px-6 md:px-12 py-8">
<h3 className="font-mono text-lg font-bold uppercase tracking-wide mb-2 text-foreground group-hover:text-background transition-all duration-200">
{t(s.titleKey)}
</h3>
<p className="text-sm text-muted-foreground group-hover:text-background/50 transition-all duration-200">
{t(s.descKey)}
</p>
</div>
<span className="hidden md:block pr-8 font-mono text-xl text-muted-foreground/30 group-hover:text-background transition-all duration-200">
&rarr;
</span>
</div>
</Link>
))}
</div>
</section>
);
}
/* ─── Quick Start Section (Code Tabs) ───────────────────────── */
const PROVIDERS = ['openai', 'claude', 'gemini'];
const PROVIDER_LABELS = { openai: 'OpenAI', claude: 'Claude', gemini: 'Gemini' };
const LANGUAGES = ['curl', 'python', 'nodejs'];
const LANG_LABELS = { curl: 'cURL', python: 'Python', nodejs: 'Node.js' };
const CODE_SAMPLES = {
openai: {
curl: `curl https://api.tokendance.space/v1/chat/completions \\
-H "Authorization: Bearer $OPENAI_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}]
}'`,
python: `from openai import OpenAI
client = OpenAI(
base_url="https://api.tokendance.space",
api_key="sk-your-key"
)
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Hello!"}]
)
print(response.choices[0].message.content)`,
nodejs: `import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.tokendance.space",
apiKey: "sk-your-key",
});
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.choices[0].message.content);`,
},
claude: {
curl: `curl https://api.tokendance.space/v1/messages \\
-H "x-api-key: sk-your-key" \\
-H "anthropic-version: 2023-06-01" \\
-H "Content-Type: application/json" \\
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello!"}]
}'`,
python: `import anthropic
client = anthropic.Anthropic(
base_url="https://api.tokendance.space",
api_key="sk-your-key"
)
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}]
)
print(response.content[0].text)`,
nodejs: `import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({
baseURL: "https://api.tokendance.space",
apiKey: "sk-your-key",
});
const response = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.content[0].text);`,
},
gemini: {
curl: `curl "https://api.tokendance.space/v1beta/models/gemini-2.0-flash:generateContent?key=sk-your-key" \\
-H "Content-Type: application/json" \\
-d '{
"contents": [{"parts":[{"text": "Hello!"}]}]
}'`,
python: `import google.generativeai as genai
genai.configure(api_key="sk-your-key")
model = genai.GenerativeModel("gemini-2.0-flash")
response = model.generate_content("Hello!")
print(response.text)`,
nodejs: `import { GoogleGenerativeAI } from "@google/generative-ai";
const genAI = new GoogleGenerativeAI("sk-your-key");
const model = genAI.getGenerativeModel({
model: "gemini-2.0-flash",
});
const result = await model.generateContent("Hello!");
console.log(result.response.text());`,
},
};
function QuickStartSection() {
const { t } = useTranslation();
const [provider, setProvider] = useState('openai');
const [lang, setLang] = useState('curl');
const [copied, setCopied] = useState(false);
const code = CODE_SAMPLES[provider]?.[lang] || '';
const handleCopy = useCallback(() => {
navigator.clipboard.writeText(code);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
}, [code]);
return (
<section id="quick-start" className="px-6 py-16">
<div className="max-w-4xl mx-auto">
<p className="font-mono text-xs text-muted-foreground tracking-widest uppercase mb-2">
Quick Start
</p>
<h2 className="font-heading text-2xl font-bold mb-8 text-foreground">
{t('快速开始')}
</h2>
{/* Provider tabs */}
<div className="flex gap-0 mb-0">
{PROVIDERS.map((p) => (
<button
key={p}
type="button"
onClick={() => setProvider(p)}
className={`font-mono text-sm px-4 py-2 transition-all duration-200 ${
provider === p
? 'bg-foreground text-background'
: 'border-2 border-border bg-transparent hover:bg-foreground hover:text-background'
}`}
>
{PROVIDER_LABELS[p]}
</button>
))}
</div>
{/* Code block */}
<div className="border-2 border-border">
<div className="flex items-center justify-between px-4 py-2.5 border-b-2 border-border bg-muted">
<div className="flex gap-3">
{LANGUAGES.map((l) => (
<button
key={l}
type="button"
onClick={() => setLang(l)}
className={`font-mono text-xs transition-colors ${
lang === l
? 'text-foreground font-bold border-b-2 border-foreground'
: 'text-muted-foreground hover:text-foreground'
}`}
>
{LANG_LABELS[l]}
</button>
))}
</div>
<button
type="button"
onClick={handleCopy}
className="text-muted-foreground hover:text-foreground transition-colors"
title="Copy"
>
{copied ? <Check size={16} className="text-green-600" /> : <Copy size={16} />}
</button>
</div>
<div className="bg-foreground text-background">
<pre className="p-5 text-sm leading-relaxed overflow-x-auto font-mono">
<code>{code}</code>
</pre>
</div>
</div>
{/* Call to docs */}
<Link
to="/pricing"
className="group mt-6 flex items-center justify-between border-2 border-border p-5 hover:bg-foreground transition-all duration-200"
>
<div>
<p className="font-mono text-xs text-muted-foreground group-hover:text-background/50 tracking-widest uppercase mb-1 transition-all duration-200">
Model Pricing
</p>
<p className="font-heading text-lg font-bold text-foreground group-hover:text-background transition-all duration-200">
{t('查看模型定价,了解完整列表')}
</p>
</div>
<span className="font-mono text-2xl text-foreground group-hover:text-background transition-all duration-200">
&rarr;
</span>
</Link>
</div>
</section>
);
}
/* ─── Pricing / CTA Section ──────────────────────────────────── */
function PricingSection() {
const { t } = useTranslation();
return (
<section id="pricing" className="py-24 px-6">
<div className="max-w-4xl mx-auto">
<div className="text-xs font-mono text-muted-foreground mb-3 tracking-widest uppercase">
{'// pricing'}
</div>
<h2 className="font-heading text-3xl font-bold md:text-4xl tracking-tight mb-16 text-foreground">
{t('获取 Token 额度')}
</h2>
{/* Token credit callout */}
<div className="bg-foreground text-background px-8 py-5 mb-6 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
<div className="flex items-center gap-4">
<span className="shrink-0 border border-background/30 px-2.5 py-0.5 text-xs font-mono tracking-widest uppercase">
{t('今日特惠')}
</span>
<span className="font-heading text-lg font-bold">
{t('注册即送 ¥10 额度')}
</span>
</div>
<span className="text-sm text-background/50">
{t('新用户注册即赠 ¥10 Token7 天有效期')}
</span>
</div>
{/* Call to action */}
<Link
to="/register"
className="group block border-2 border-border p-8 mb-6 hover:bg-foreground transition-all duration-200"
>
<h3 className="font-heading text-2xl font-bold mb-3 text-foreground group-hover:text-background transition-all duration-200">
{t('立即注册,开始使用')}
</h3>
<p className="text-sm text-muted-foreground group-hover:text-background/60 leading-relaxed mb-6 transition-all duration-200">
{t(
'兼容 OpenAI / Claude / Gemini 协议。一个 API Key调用所有主流模型。免去多平台充值与管理的麻烦。'
)}
</p>
<span className="inline-flex items-center bg-foreground text-background border-2 border-foreground px-6 py-3 text-sm font-mono font-bold uppercase tracking-widest group-hover:bg-background group-hover:text-foreground transition-colors duration-200">
{t('免费注册')} &rarr;
</span>
</Link>
</div>
</section>
);
}
/* ─── Providers Marquee Section ──────────────────────────────── */
const PROVIDER_LOGOS = [
{ name: 'OpenAI', className: 'opacity-60' },
{ name: 'Claude', className: 'opacity-60' },
{ name: 'Gemini', className: 'opacity-60' },
{ name: 'DeepSeek', className: 'opacity-60' },
{ name: 'Qwen', className: 'opacity-60' },
{ name: 'MiniMax', className: 'opacity-60' },
{ name: 'Kimi', className: 'opacity-60' },
{ name: '智谱', className: 'opacity-60' },
{ name: 'Groq', className: 'opacity-60' },
{ name: 'Azure', className: 'opacity-60' },
];
function ProvidersSection() {
const { t } = useTranslation();
const navigate = useNavigate();
return (
<section className="bg-foreground text-background border-t-4 border-background">
{/* Provider logos marquee area */}
<div className="w-full py-16 px-6">
<div className="max-w-6xl mx-auto text-center">
<p className="font-mono text-xs text-background/30 tracking-widest uppercase mb-3">
{'// supported providers'}
</p>
<h2 className="font-heading text-2xl font-bold mb-8">
{t('40+ AI 供应商,统一接入')}
</h2>
<div className="overflow-hidden">
<div className="home-marquee flex gap-16 items-center">
{[...PROVIDER_LOGOS, ...PROVIDER_LOGOS].map((logo, i) => (
<span
key={i}
className="font-mono text-sm text-background/40 whitespace-nowrap tracking-wider"
>
{logo.name}
</span>
))}
</div>
</div>
<div className="mt-10">
<button
type="button"
onClick={() => navigate('/pricing')}
className="inline-flex items-center bg-background text-foreground border-2 border-background px-8 py-3 text-sm font-mono font-bold uppercase tracking-widest hover:bg-transparent hover:text-background transition-colors duration-200"
>
{t('查看全部模型')} &rarr;
</button>
</div>
</div>
</div>
<div className="text-center pt-6 pb-8">
<p className="font-mono text-xs text-background/30 tracking-wide">
{t('免费注册,分钟级接入。兼容你现有的 SDK 和工作流。')}
</p>
</div>
</section>
);
}
/* ─── Dark Footer ────────────────────────────────────────────── */
function DarkFooter() {
const { t } = useTranslation();
return (
<footer className="bg-foreground text-background border-t border-background/10 py-12 px-6">
<div className="max-w-6xl mx-auto">
<div className="flex flex-col md:flex-row items-center justify-between gap-6 mb-8">
<div className="flex items-center gap-6">
<Link
to="/pricing"
className="font-mono text-xs text-background/40 hover:text-background transition-all duration-200"
>
{t('定价')}
</Link>
<Link
to="/console"
className="font-mono text-xs text-background/40 hover:text-background transition-all duration-200"
>
{t('控制台')}
</Link>
</div>
</div>
<div className="border-t border-background/10 pt-6 flex flex-col md:flex-row items-center justify-between gap-4">
<span className="font-mono text-xs text-background/30">
&copy; 2026 TokenFactory
</span>
</div>
</div>
</footer>
);
}
/* ─── Main Home Page ────────────────────────────────────────── */
const Home = () => {
const { t } = useTranslation();
return (
<div className="min-h-screen bg-background text-foreground">
<div className="home-page">
<HeroSection />
<FeaturesSection />
<HowItWorksSection />
<QuickStartSection />
<PricingSection />
<ProvidersSection />
<DarkFooter />
</div>
</div>
);
};
export default Home;