775 lines
32 KiB
JavaScript
775 lines
32 KiB
JavaScript
/*
|
||
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("查看价格")} →
|
||
</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">
|
||
→
|
||
</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">
|
||
→
|
||
</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 Token,7 天有效期')}
|
||
</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('免费注册')} →
|
||
</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('查看全部模型')} →
|
||
</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">
|
||
© 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;
|
||
|