mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-03 19:11:02 +08:00
feat: redesign landing page UI and improve header
This commit is contained in:
@@ -197,8 +197,12 @@ func (s *TraderStore) UpdateCustomPrompt(userID, id string, customPrompt string,
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete deletes trader
|
||||
// Delete deletes trader and associated data
|
||||
func (s *TraderStore) Delete(userID, id string) error {
|
||||
// Delete associated equity snapshots first
|
||||
_, _ = s.db.Exec(`DELETE FROM trader_equity_snapshots WHERE trader_id = ?`, id)
|
||||
|
||||
// Delete the trader
|
||||
_, err := s.db.Exec(`DELETE FROM traders WHERE id = ? AND user_id = ?`, id, userID)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ import {
|
||||
BookOpen,
|
||||
HelpCircle,
|
||||
Pencil,
|
||||
UserPlus,
|
||||
ExternalLink,
|
||||
} from 'lucide-react'
|
||||
import { confirmToast } from '../lib/notify'
|
||||
import { toast } from 'sonner'
|
||||
@@ -1611,6 +1613,16 @@ function ExchangeConfigModal({
|
||||
(e) => e.id === selectedExchangeId
|
||||
)
|
||||
|
||||
// 交易所注册链接配置
|
||||
const exchangeRegistrationLinks: Record<string, { url: string; hasReferral?: boolean }> = {
|
||||
binance: { url: 'https://www.binance.com/join?ref=NOFXAI', hasReferral: true },
|
||||
okx: { url: 'https://www.okx.com/join/1865360', hasReferral: true },
|
||||
bybit: { url: 'https://partner.bybit.com/b/83856', hasReferral: true },
|
||||
hyperliquid: { url: 'https://app.hyperliquid.xyz/join/AITRADING', hasReferral: true },
|
||||
aster: { url: 'https://www.asterdex.com/en/referral/fdfc0e', hasReferral: true },
|
||||
lighter: { url: 'https://lighter.xyz', hasReferral: false },
|
||||
}
|
||||
|
||||
// 如果是编辑现有交易所,初始化表单数据
|
||||
useEffect(() => {
|
||||
if (editingExchangeId && selectedExchange) {
|
||||
@@ -1918,6 +1930,35 @@ function ExchangeConfigModal({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 注册链接 */}
|
||||
{exchangeRegistrationLinks[selectedExchange.id] && (
|
||||
<a
|
||||
href={exchangeRegistrationLinks[selectedExchange.id].url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-between p-3 rounded-lg transition-all hover:scale-[1.02]"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.08)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<UserPlus className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-sm" style={{ color: '#EAECEF' }}>
|
||||
{language === 'zh' ? '还没有交易所账号?点击注册' : "No exchange account? Register here"}
|
||||
</span>
|
||||
{exchangeRegistrationLinks[selectedExchange.id].hasReferral && (
|
||||
<span
|
||||
className="text-xs px-1.5 py-0.5 rounded"
|
||||
style={{ background: 'rgba(14, 203, 129, 0.2)', color: '#0ECB81' }}
|
||||
>
|
||||
{language === 'zh' ? '折扣优惠' : 'Discount'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<ExternalLink className="w-4 h-4" style={{ color: '#848E9C' }} />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Menu, X, ChevronDown } from 'lucide-react'
|
||||
import { t, type Language } from '../i18n/translations'
|
||||
import { Container } from './Container'
|
||||
import { useSystemConfig } from '../hooks/useSystemConfig'
|
||||
import { OFFICIAL_LINKS } from '../constants/branding'
|
||||
|
||||
type Page =
|
||||
| 'competition'
|
||||
@@ -430,45 +431,77 @@ export default function HeaderBar({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right Side - Original Navigation Items and Login */}
|
||||
<div className="flex items-center gap-6">
|
||||
{/* Only show original navigation items on home page */}
|
||||
{isHomePage &&
|
||||
[
|
||||
{ key: 'features', label: t('features', language) },
|
||||
{ key: 'howItWorks', label: t('howItWorks', language) },
|
||||
{ key: 'GitHub', label: 'GitHub' },
|
||||
{ key: 'community', label: t('community', language) },
|
||||
].map((item) => (
|
||||
<a
|
||||
key={item.key}
|
||||
href={
|
||||
item.key === 'GitHub'
|
||||
? 'https://github.com/tinkle-community/nofx'
|
||||
: item.key === 'community'
|
||||
? 'https://t.me/nofx_dev_community'
|
||||
: `#${item.key === 'features' ? 'features' : 'how-it-works'}`
|
||||
}
|
||||
target={
|
||||
item.key === 'GitHub' || item.key === 'community'
|
||||
? '_blank'
|
||||
: undefined
|
||||
}
|
||||
rel={
|
||||
item.key === 'GitHub' || item.key === 'community'
|
||||
? 'noopener noreferrer'
|
||||
: undefined
|
||||
}
|
||||
className="text-sm transition-colors relative group"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
>
|
||||
{item.label}
|
||||
<span
|
||||
className="absolute -bottom-1 left-0 w-0 h-0.5 group-hover:w-full transition-all duration-300"
|
||||
style={{ background: 'var(--brand-yellow)' }}
|
||||
/>
|
||||
</a>
|
||||
))}
|
||||
{/* Right Side - Social Links and User Actions */}
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Social Links - Always visible */}
|
||||
<div className="flex items-center gap-1">
|
||||
{/* GitHub */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-2 rounded-lg transition-all hover:scale-110"
|
||||
style={{ color: '#848E9C' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = '#EAECEF'
|
||||
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.05)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.background = 'transparent'
|
||||
}}
|
||||
title="GitHub"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
|
||||
</svg>
|
||||
</a>
|
||||
{/* Twitter/X */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-2 rounded-lg transition-all hover:scale-110"
|
||||
style={{ color: '#848E9C' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = '#1DA1F2'
|
||||
e.currentTarget.style.background = 'rgba(29, 161, 242, 0.1)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.background = 'transparent'
|
||||
}}
|
||||
title="Twitter"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
</a>
|
||||
{/* Telegram */}
|
||||
<a
|
||||
href={OFFICIAL_LINKS.telegram}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-2 rounded-lg transition-all hover:scale-110"
|
||||
style={{ color: '#848E9C' }}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = '#0088cc'
|
||||
e.currentTarget.style.background = 'rgba(0, 136, 204, 0.1)'
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = '#848E9C'
|
||||
e.currentTarget.style.background = 'transparent'
|
||||
}}
|
||||
title="Telegram"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Divider */}
|
||||
<div className="h-5 w-px" style={{ background: '#2B3139' }} />
|
||||
|
||||
{/* User Info and Actions */}
|
||||
{isLoggedIn && user ? (
|
||||
@@ -932,28 +965,10 @@ export default function HeaderBar({
|
||||
[
|
||||
{ key: 'features', label: t('features', language) },
|
||||
{ key: 'howItWorks', label: t('howItWorks', language) },
|
||||
{ key: 'GitHub', label: 'GitHub' },
|
||||
{ key: 'community', label: t('community', language) },
|
||||
].map((item) => (
|
||||
<a
|
||||
key={item.key}
|
||||
href={
|
||||
item.key === 'GitHub'
|
||||
? 'https://github.com/tinkle-community/nofx'
|
||||
: item.key === 'community'
|
||||
? 'https://t.me/nofx_dev_community'
|
||||
: `#${item.key === 'features' ? 'features' : 'how-it-works'}`
|
||||
}
|
||||
target={
|
||||
item.key === 'GitHub' || item.key === 'community'
|
||||
? '_blank'
|
||||
: undefined
|
||||
}
|
||||
rel={
|
||||
item.key === 'GitHub' || item.key === 'community'
|
||||
? 'noopener noreferrer'
|
||||
: undefined
|
||||
}
|
||||
href={`#${item.key === 'features' ? 'features' : 'how-it-works'}`}
|
||||
className="block text-sm py-2"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
>
|
||||
@@ -961,6 +976,43 @@ export default function HeaderBar({
|
||||
</a>
|
||||
))}
|
||||
|
||||
{/* Social Links - Mobile */}
|
||||
<div className="py-3 flex items-center gap-3" style={{ borderTop: '1px solid #2B3139' }}>
|
||||
<a
|
||||
href={OFFICIAL_LINKS.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-2 rounded-lg"
|
||||
style={{ color: '#848E9C', background: 'rgba(255, 255, 255, 0.05)' }}
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 16 16" fill="currentColor">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href={OFFICIAL_LINKS.twitter}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-2 rounded-lg"
|
||||
style={{ color: '#848E9C', background: 'rgba(255, 255, 255, 0.05)' }}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href={OFFICIAL_LINKS.telegram}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="p-2 rounded-lg"
|
||||
style={{ color: '#848E9C', background: 'rgba(255, 255, 255, 0.05)' }}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Language Toggle */}
|
||||
<div className="py-2">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { AIModel, Exchange, CreateTraderRequest, Strategy } from '../types'
|
||||
import { useLanguage } from '../contexts/LanguageContext'
|
||||
import { t } from '../i18n/translations'
|
||||
import { toast } from 'sonner'
|
||||
import { Pencil, Plus, X as IconX, Sparkles } from 'lucide-react'
|
||||
import { Pencil, Plus, X as IconX, Sparkles, ExternalLink, UserPlus } from 'lucide-react'
|
||||
import { httpClient } from '../lib/httpClient'
|
||||
|
||||
// 提取下划线后面的名称部分
|
||||
@@ -12,6 +12,16 @@ function getShortName(fullName: string): string {
|
||||
return parts.length > 1 ? parts[parts.length - 1] : fullName
|
||||
}
|
||||
|
||||
// 交易所注册链接配置
|
||||
const EXCHANGE_REGISTRATION_LINKS: Record<string, { url: string; hasReferral?: boolean }> = {
|
||||
binance: { url: 'https://www.binance.com/join?ref=NOFXENG', hasReferral: true },
|
||||
okx: { url: 'https://www.okx.com/join/1865360', hasReferral: true },
|
||||
bybit: { url: 'https://partner.bybit.com/b/83856', hasReferral: true },
|
||||
hyperliquid: { url: 'https://app.hyperliquid.xyz/join/AITRADING', hasReferral: true },
|
||||
aster: { url: 'https://www.asterdex.com/en/referral/fdfc0e', hasReferral: true },
|
||||
lighter: { url: 'https://lighter.xyz', hasReferral: false },
|
||||
}
|
||||
|
||||
import type { TraderConfigData } from '../types'
|
||||
|
||||
// 表单内部状态类型
|
||||
@@ -272,6 +282,30 @@ export function TraderConfigModal({
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{/* Exchange Registration Link */}
|
||||
{formData.exchange_id && (() => {
|
||||
// Exchange ID is the exchange type (e.g., "binance", "okx", "aster")
|
||||
const exchangeType = formData.exchange_id.toLowerCase()
|
||||
const regLink = EXCHANGE_REGISTRATION_LINKS[exchangeType]
|
||||
if (!regLink) return null
|
||||
return (
|
||||
<a
|
||||
href={regLink.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-2 inline-flex items-center gap-1.5 text-xs text-[#848E9C] hover:text-[#F0B90B] transition-colors"
|
||||
>
|
||||
<UserPlus className="w-3.5 h-3.5" />
|
||||
<span>还没有交易所账号?点击注册</span>
|
||||
{regLink.hasReferral && (
|
||||
<span className="px-1.5 py-0.5 bg-[#F0B90B]/10 text-[#F0B90B] rounded text-[10px]">
|
||||
折扣优惠
|
||||
</span>
|
||||
)}
|
||||
<ExternalLink className="w-3 h-3" />
|
||||
</a>
|
||||
)
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Shield, Target } from 'lucide-react'
|
||||
import AnimatedSection from './AnimatedSection'
|
||||
import Typewriter from '../Typewriter'
|
||||
import { Terminal, Shield, Cpu, BarChart3 } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
|
||||
interface AboutSectionProps {
|
||||
@@ -9,119 +7,151 @@ interface AboutSectionProps {
|
||||
}
|
||||
|
||||
export default function AboutSection({ language }: AboutSectionProps) {
|
||||
const features = [
|
||||
{
|
||||
icon: Shield,
|
||||
title: language === 'zh' ? '完全自主控制' : 'Full Control',
|
||||
desc: language === 'zh' ? '自托管,数据安全' : 'Self-hosted, data secure',
|
||||
},
|
||||
{
|
||||
icon: Cpu,
|
||||
title: language === 'zh' ? '多 AI 支持' : 'Multi-AI Support',
|
||||
desc: language === 'zh' ? 'DeepSeek, GPT, Claude...' : 'DeepSeek, GPT, Claude...',
|
||||
},
|
||||
{
|
||||
icon: BarChart3,
|
||||
title: language === 'zh' ? '实时监控' : 'Real-time Monitor',
|
||||
desc: language === 'zh' ? '可视化交易看板' : 'Visual trading dashboard',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<AnimatedSection id="about" backgroundColor="var(--brand-dark-gray)">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
<section className="py-24 relative overflow-hidden" style={{ background: '#0B0E11' }}>
|
||||
{/* Background Decoration */}
|
||||
<div
|
||||
className="absolute top-0 right-0 w-96 h-96 rounded-full blur-3xl opacity-30"
|
||||
style={{ background: 'radial-gradient(circle, rgba(240, 185, 11, 0.1) 0%, transparent 70%)' }}
|
||||
/>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-4">
|
||||
<div className="grid lg:grid-cols-2 gap-16 items-center">
|
||||
{/* Left Content */}
|
||||
<motion.div
|
||||
className="space-y-6"
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<motion.div
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full"
|
||||
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full mb-6"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
||||
}}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
>
|
||||
<Target
|
||||
className="w-4 h-4"
|
||||
style={{ color: 'var(--brand-yellow)' }}
|
||||
/>
|
||||
<span
|
||||
className="text-sm font-semibold"
|
||||
style={{ color: 'var(--brand-yellow)' }}
|
||||
>
|
||||
<Terminal className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#F0B90B' }}>
|
||||
{t('aboutNofx', language)}
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
<h2
|
||||
className="text-4xl font-bold"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
>
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-6" style={{ color: '#EAECEF' }}>
|
||||
{t('whatIsNofx', language)}
|
||||
</h2>
|
||||
<p
|
||||
className="text-lg leading-relaxed"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
>
|
||||
{t('nofxNotAnotherBot', language)}{' '}
|
||||
{t('nofxDescription1', language)}{' '}
|
||||
{t('nofxDescription2', language)}
|
||||
|
||||
<p className="text-lg mb-8 leading-relaxed" style={{ color: '#848E9C' }}>
|
||||
{t('nofxNotAnotherBot', language)} {t('nofxDescription1', language)}
|
||||
</p>
|
||||
<p
|
||||
className="text-lg leading-relaxed"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
>
|
||||
{t('nofxDescription3', language)}{' '}
|
||||
{t('nofxDescription4', language)}{' '}
|
||||
{t('nofxDescription5', language)}
|
||||
</p>
|
||||
<motion.div
|
||||
className="flex items-center gap-3 pt-4"
|
||||
whileHover={{ x: 5 }}
|
||||
>
|
||||
<div
|
||||
className="w-12 h-12 rounded-full flex items-center justify-center"
|
||||
style={{ background: 'rgba(240, 185, 11, 0.1)' }}
|
||||
>
|
||||
<Shield
|
||||
className="w-6 h-6"
|
||||
style={{ color: 'var(--brand-yellow)' }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
className="font-semibold"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
|
||||
{/* Feature Pills */}
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="flex items-center gap-3 px-4 py-3 rounded-xl"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.03)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.06)',
|
||||
}}
|
||||
>
|
||||
{t('youFullControl', language)}
|
||||
</div>
|
||||
<div
|
||||
className="text-sm"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
>
|
||||
{t('fullControlDesc', language)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg flex items-center justify-center"
|
||||
style={{ background: 'rgba(240, 185, 11, 0.1)' }}
|
||||
>
|
||||
<feature.icon className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-semibold" style={{ color: '#EAECEF' }}>
|
||||
{feature.title}
|
||||
</div>
|
||||
<div className="text-xs" style={{ color: '#5E6673' }}>
|
||||
{feature.desc}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="relative">
|
||||
{/* Right - Terminal */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
<div
|
||||
className="rounded-2xl p-8"
|
||||
className="rounded-2xl overflow-hidden"
|
||||
style={{
|
||||
background: 'var(--brand-black)',
|
||||
border: '1px solid var(--panel-border)',
|
||||
background: '#0D1117',
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5)',
|
||||
}}
|
||||
>
|
||||
<Typewriter
|
||||
lines={[
|
||||
'$ git clone https://github.com/tinkle-community/nofx.git',
|
||||
'$ cd nofx',
|
||||
'$ chmod +x start.sh',
|
||||
'$ ./start.sh start --build',
|
||||
t('startupMessages1', language),
|
||||
t('startupMessages2', language),
|
||||
t('startupMessages3', language),
|
||||
]}
|
||||
typingSpeed={70}
|
||||
lineDelay={900}
|
||||
className="text-sm font-mono"
|
||||
style={{
|
||||
color: '#00FF88',
|
||||
textShadow: '0 0 8px rgba(0,255,136,0.4)',
|
||||
}}
|
||||
/>
|
||||
{/* Terminal Header */}
|
||||
<div
|
||||
className="flex items-center gap-2 px-4 py-3"
|
||||
style={{ background: 'rgba(255, 255, 255, 0.03)', borderBottom: '1px solid rgba(255, 255, 255, 0.06)' }}
|
||||
>
|
||||
<div className="flex gap-2">
|
||||
<div className="w-3 h-3 rounded-full" style={{ background: '#FF5F56' }} />
|
||||
<div className="w-3 h-3 rounded-full" style={{ background: '#FFBD2E' }} />
|
||||
<div className="w-3 h-3 rounded-full" style={{ background: '#27C93F' }} />
|
||||
</div>
|
||||
<span className="text-xs ml-2" style={{ color: '#5E6673' }}>terminal</span>
|
||||
</div>
|
||||
|
||||
{/* Terminal Content */}
|
||||
<div className="p-6 font-mono text-sm space-y-2">
|
||||
<div style={{ color: '#5E6673' }}>$ git clone https://github.com/NoFxAiOS/nofx.git</div>
|
||||
<div style={{ color: '#5E6673' }}>$ cd nofx && chmod +x start.sh</div>
|
||||
<div style={{ color: '#5E6673' }}>$ ./start.sh start --build</div>
|
||||
<div className="pt-2" style={{ color: '#F0B90B' }}>
|
||||
✓ {t('startupMessages1', language)}
|
||||
</div>
|
||||
<div style={{ color: '#0ECB81' }}>
|
||||
✓ {t('startupMessages2', language)}
|
||||
</div>
|
||||
<div style={{ color: '#0ECB81' }}>
|
||||
✓ {t('startupMessages3', language)}
|
||||
</div>
|
||||
<motion.div
|
||||
className="flex items-center gap-2 pt-2"
|
||||
animate={{ opacity: [1, 0.5, 1] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity }}
|
||||
>
|
||||
<span style={{ color: '#F0B90B' }}>▸</span>
|
||||
<span style={{ color: '#EAECEF' }}>_</span>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</AnimatedSection>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import AnimatedSection from './AnimatedSection'
|
||||
import { MessageCircle, Heart, Repeat2, ExternalLink } from 'lucide-react'
|
||||
import { Language } from '../../i18n/translations'
|
||||
|
||||
interface CardProps {
|
||||
interface TweetProps {
|
||||
quote: string
|
||||
authorName: string
|
||||
handle: string
|
||||
@@ -10,49 +11,94 @@ interface CardProps {
|
||||
delay: number
|
||||
}
|
||||
|
||||
function TestimonialCard({ quote, authorName, delay }: CardProps) {
|
||||
function TweetCard({ quote, authorName, handle, avatarUrl, tweetUrl, delay }: TweetProps) {
|
||||
return (
|
||||
<motion.div
|
||||
className="p-6 rounded-xl"
|
||||
<motion.a
|
||||
href={tweetUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block p-5 rounded-2xl transition-all duration-300 group"
|
||||
style={{
|
||||
background: 'var(--brand-dark-gray)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.1)',
|
||||
background: '#12161C',
|
||||
border: '1px solid rgba(255, 255, 255, 0.06)',
|
||||
}}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay }}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileHover={{
|
||||
y: -4,
|
||||
borderColor: 'rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
>
|
||||
<p className="text-lg mb-4" style={{ color: 'var(--brand-light-gray)' }}>
|
||||
"{quote}"
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<img
|
||||
src={avatarUrl}
|
||||
alt={authorName}
|
||||
className="w-10 h-10 rounded-full object-cover"
|
||||
style={{ border: '2px solid rgba(255, 255, 255, 0.1)' }}
|
||||
/>
|
||||
<div>
|
||||
<div className="font-semibold text-sm" style={{ color: '#EAECEF' }}>
|
||||
{authorName}
|
||||
</div>
|
||||
<div className="text-xs" style={{ color: '#5E6673' }}>
|
||||
{handle}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* X Logo */}
|
||||
<div
|
||||
className="w-8 h-8 rounded-full"
|
||||
style={{ background: 'var(--binance-yellow)' }}
|
||||
/>
|
||||
<span
|
||||
className="text-sm font-semibold"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
className="w-6 h-6 flex items-center justify-center opacity-50 group-hover:opacity-100 transition-opacity"
|
||||
style={{ color: '#EAECEF' }}
|
||||
>
|
||||
{authorName}
|
||||
</span>
|
||||
<svg viewBox="0 0 24 24" className="w-4 h-4" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Content */}
|
||||
<p
|
||||
className="text-sm leading-relaxed mb-4 line-clamp-4"
|
||||
style={{ color: '#B7BDC6' }}
|
||||
>
|
||||
{quote}
|
||||
</p>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex items-center gap-6 pt-3" style={{ borderTop: '1px solid rgba(255, 255, 255, 0.05)' }}>
|
||||
<div className="flex items-center gap-1.5 text-xs" style={{ color: '#5E6673' }}>
|
||||
<MessageCircle className="w-3.5 h-3.5" />
|
||||
<span>Reply</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-xs" style={{ color: '#5E6673' }}>
|
||||
<Repeat2 className="w-3.5 h-3.5" />
|
||||
<span>Repost</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-xs" style={{ color: '#5E6673' }}>
|
||||
<Heart className="w-3.5 h-3.5" />
|
||||
<span>Like</span>
|
||||
</div>
|
||||
<div className="ml-auto opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<ExternalLink className="w-3.5 h-3.5" style={{ color: '#F0B90B' }} />
|
||||
</div>
|
||||
</div>
|
||||
</motion.a>
|
||||
)
|
||||
}
|
||||
|
||||
export default function CommunitySection() {
|
||||
const staggerContainer = {
|
||||
animate: { transition: { staggerChildren: 0.1 } },
|
||||
}
|
||||
interface CommunitySectionProps {
|
||||
language?: Language
|
||||
}
|
||||
|
||||
// 推特内容整合(保持原三列布局,超出自动换行)
|
||||
const items: CardProps[] = [
|
||||
export default function CommunitySection({ language }: CommunitySectionProps) {
|
||||
const tweets: TweetProps[] = [
|
||||
{
|
||||
quote:
|
||||
'前不久非常火的 AI 量化交易系统 NOF1,在 GitHub 上有人将其复刻并开源,这就是 NOFX 项目。基于 DeepSeek、Qwen 等大语言模型,打造的通用架构 AI 交易操作系统,完成了从决策、到交易、再到复盘的闭环。GitHub: https://github.com/NoFxAiOS/nofx',
|
||||
'前不久非常火的 AI 量化交易系统 NOF1,在 GitHub 上有人将其复刻并开源,这就是 NOFX 项目。基于 DeepSeek、Qwen 等大语言模型,打造的通用架构 AI 交易操作系统,完成了从决策、到交易、再到复盘的闭环。',
|
||||
authorName: 'Michael Williams',
|
||||
handle: '@MichaelWil93725',
|
||||
avatarUrl:
|
||||
@@ -84,20 +130,61 @@ export default function CommunitySection() {
|
||||
]
|
||||
|
||||
return (
|
||||
<AnimatedSection>
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<section className="py-24 relative" style={{ background: '#0B0E11' }}>
|
||||
{/* Background Decoration */}
|
||||
<div
|
||||
className="absolute right-0 top-1/2 -translate-y-1/2 w-96 h-96 rounded-full blur-3xl opacity-20"
|
||||
style={{ background: 'radial-gradient(circle, rgba(29, 161, 242, 0.1) 0%, transparent 70%)' }}
|
||||
/>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-4 relative z-10">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
className="grid md:grid-cols-3 gap-6"
|
||||
variants={staggerContainer}
|
||||
initial="initial"
|
||||
whileInView="animate"
|
||||
className="text-center mb-12"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
{items.map((item, idx) => (
|
||||
<TestimonialCard key={idx} {...item} />
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#EAECEF' }}>
|
||||
{language === 'zh' ? '社区声音' : 'Community Voices'}
|
||||
</h2>
|
||||
<p className="text-lg" style={{ color: '#848E9C' }}>
|
||||
{language === 'zh' ? '看看大家怎么说' : 'See what others are saying'}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Tweet Grid */}
|
||||
<div className="grid md:grid-cols-3 gap-5">
|
||||
{tweets.map((tweet, idx) => (
|
||||
<TweetCard key={idx} {...tweet} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<motion.div
|
||||
className="text-center mt-12"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<a
|
||||
href="https://twitter.com/nofx_official"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 rounded-xl font-medium transition-all hover:scale-105"
|
||||
style={{
|
||||
background: 'rgba(29, 161, 242, 0.1)',
|
||||
color: '#1DA1F2',
|
||||
border: '1px solid rgba(29, 161, 242, 0.3)',
|
||||
}}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" className="w-5 h-5" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
{language === 'zh' ? '关注我们的 X' : 'Follow us on X'}
|
||||
</a>
|
||||
</motion.div>
|
||||
</div>
|
||||
</AnimatedSection>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import AnimatedSection from './AnimatedSection'
|
||||
import { CryptoFeatureCard } from '../CryptoFeatureCard'
|
||||
import { Code, Cpu, Lock, Rocket } from 'lucide-react'
|
||||
import { Brain, Swords, BarChart3, Shield, Blocks, LineChart } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
|
||||
interface FeaturesSectionProps {
|
||||
@@ -9,84 +7,199 @@ interface FeaturesSectionProps {
|
||||
}
|
||||
|
||||
export default function FeaturesSection({ language }: FeaturesSectionProps) {
|
||||
const features = [
|
||||
{
|
||||
icon: Brain,
|
||||
title: language === 'zh' ? 'AI 策略编排引擎' : 'AI Strategy Orchestration',
|
||||
desc: language === 'zh'
|
||||
? '支持 DeepSeek、GPT、Claude、Qwen 等多种大模型,自定义 Prompt 策略,AI 自主分析市场并做出交易决策'
|
||||
: 'Support DeepSeek, GPT, Claude, Qwen and more. Custom prompts, AI autonomously analyzes markets and makes trading decisions',
|
||||
highlight: true,
|
||||
badge: language === 'zh' ? '核心能力' : 'Core',
|
||||
},
|
||||
{
|
||||
icon: Swords,
|
||||
title: language === 'zh' ? '多 AI 竞技场' : 'Multi-AI Arena',
|
||||
desc: language === 'zh'
|
||||
? '多个 AI 交易员同台竞技,实时 PnL 排行榜,自动优胜劣汰,让最强策略脱颖而出'
|
||||
: 'Multiple AI traders compete in real-time, live PnL leaderboard, automatic survival of the fittest',
|
||||
highlight: true,
|
||||
badge: language === 'zh' ? '独创' : 'Unique',
|
||||
},
|
||||
{
|
||||
icon: LineChart,
|
||||
title: language === 'zh' ? '专业量化数据' : 'Pro Quant Data',
|
||||
desc: language === 'zh'
|
||||
? '集成 K线、技术指标、市场深度、资金费率、持仓量等专业量化数据,为 AI 决策提供全面信息'
|
||||
: 'Integrated candlesticks, indicators, order book, funding rates, open interest - comprehensive data for AI decisions',
|
||||
highlight: true,
|
||||
badge: language === 'zh' ? '专业' : 'Pro',
|
||||
},
|
||||
{
|
||||
icon: Blocks,
|
||||
title: language === 'zh' ? '多交易所支持' : 'Multi-Exchange Support',
|
||||
desc: language === 'zh'
|
||||
? 'Binance、OKX、Bybit、Hyperliquid、Aster DEX,一套系统管理多个交易所'
|
||||
: 'Binance, OKX, Bybit, Hyperliquid, Aster DEX - one system, multiple exchanges',
|
||||
},
|
||||
{
|
||||
icon: BarChart3,
|
||||
title: language === 'zh' ? '实时可视化看板' : 'Real-time Dashboard',
|
||||
desc: language === 'zh'
|
||||
? '交易监控、收益曲线、持仓分析、AI 决策日志,一目了然'
|
||||
: 'Trade monitoring, PnL curves, position analysis, AI decision logs at a glance',
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: language === 'zh' ? '开源自托管' : 'Open Source & Self-Hosted',
|
||||
desc: language === 'zh'
|
||||
? '代码完全开源可审计,数据存储在本地,API 密钥不经过第三方'
|
||||
: 'Fully open source, data stored locally, API keys never leave your server',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<AnimatedSection id="features">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<section className="py-24 relative" style={{ background: '#0B0E11' }}>
|
||||
{/* Background */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.02]"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(#F0B90B 1px, transparent 1px), linear-gradient(90deg, #F0B90B 1px, transparent 1px)`,
|
||||
backgroundSize: '40px 40px',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-4 relative z-10">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
className="text-center mb-16"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<motion.div
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full mb-6"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
||||
}}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
>
|
||||
<Rocket
|
||||
className="w-4 h-4"
|
||||
style={{ color: 'var(--brand-yellow)' }}
|
||||
/>
|
||||
<span
|
||||
className="text-sm font-semibold"
|
||||
style={{ color: 'var(--brand-yellow)' }}
|
||||
>
|
||||
{t('coreFeatures', language)}
|
||||
</span>
|
||||
</motion.div>
|
||||
<h2
|
||||
className="text-4xl font-bold mb-4"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
>
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#EAECEF' }}>
|
||||
{t('whyChooseNofx', language)}
|
||||
</h2>
|
||||
<p className="text-lg" style={{ color: 'var(--text-secondary)' }}>
|
||||
{t('openCommunityDriven', language)}
|
||||
<p className="text-lg max-w-2xl mx-auto" style={{ color: '#848E9C' }}>
|
||||
{language === 'zh'
|
||||
? '不只是交易机器人,而是完整的 AI 交易操作系统'
|
||||
: 'Not just a trading bot, but a complete AI trading operating system'}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
||||
<CryptoFeatureCard
|
||||
icon={<Code className="w-8 h-8" />}
|
||||
title={t('openSourceSelfHosted', language)}
|
||||
description={t('openSourceDesc', language)}
|
||||
features={[
|
||||
t('openSourceFeatures1', language),
|
||||
t('openSourceFeatures2', language),
|
||||
t('openSourceFeatures3', language),
|
||||
t('openSourceFeatures4', language),
|
||||
]}
|
||||
delay={0}
|
||||
/>
|
||||
<CryptoFeatureCard
|
||||
icon={<Cpu className="w-8 h-8" />}
|
||||
title={t('multiAgentCompetition', language)}
|
||||
description={t('multiAgentDesc', language)}
|
||||
features={[
|
||||
t('multiAgentFeatures1', language),
|
||||
t('multiAgentFeatures2', language),
|
||||
t('multiAgentFeatures3', language),
|
||||
t('multiAgentFeatures4', language),
|
||||
]}
|
||||
delay={0.1}
|
||||
/>
|
||||
<CryptoFeatureCard
|
||||
icon={<Lock className="w-8 h-8" />}
|
||||
title={t('secureReliableTrading', language)}
|
||||
description={t('secureDesc', language)}
|
||||
features={[
|
||||
t('secureFeatures1', language),
|
||||
t('secureFeatures2', language),
|
||||
t('secureFeatures3', language),
|
||||
t('secureFeatures4', language),
|
||||
]}
|
||||
delay={0.2}
|
||||
/>
|
||||
{/* Features Grid */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className={`
|
||||
relative group rounded-2xl p-6 transition-all duration-300
|
||||
${feature.highlight ? 'md:col-span-1 lg:col-span-1' : ''}
|
||||
`}
|
||||
style={{
|
||||
background: feature.highlight
|
||||
? 'linear-gradient(135deg, rgba(240, 185, 11, 0.08) 0%, rgba(240, 185, 11, 0.02) 100%)'
|
||||
: '#12161C',
|
||||
border: feature.highlight
|
||||
? '1px solid rgba(240, 185, 11, 0.2)'
|
||||
: '1px solid rgba(255, 255, 255, 0.06)',
|
||||
}}
|
||||
>
|
||||
{/* Badge */}
|
||||
{feature.badge && (
|
||||
<div
|
||||
className="absolute top-4 right-4 px-2 py-1 rounded text-xs font-medium"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.15)',
|
||||
color: '#F0B90B',
|
||||
}}
|
||||
>
|
||||
{feature.badge}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Icon */}
|
||||
<motion.div
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center mb-4"
|
||||
style={{
|
||||
background: feature.highlight
|
||||
? 'rgba(240, 185, 11, 0.15)'
|
||||
: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
||||
}}
|
||||
whileHover={{ scale: 1.1, rotate: 5 }}
|
||||
>
|
||||
<feature.icon
|
||||
className="w-6 h-6"
|
||||
style={{ color: '#F0B90B' }}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Text */}
|
||||
<h3
|
||||
className="text-xl font-bold mb-3"
|
||||
style={{ color: '#EAECEF' }}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
className="text-sm leading-relaxed"
|
||||
style={{ color: '#848E9C' }}
|
||||
>
|
||||
{feature.desc}
|
||||
</p>
|
||||
|
||||
{/* Hover Glow */}
|
||||
<div
|
||||
className="absolute -bottom-10 -right-10 w-32 h-32 rounded-full blur-3xl opacity-0 group-hover:opacity-30 transition-opacity duration-500"
|
||||
style={{ background: '#F0B90B' }}
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Bottom Stats */}
|
||||
<motion.div
|
||||
className="mt-16 grid grid-cols-2 md:grid-cols-4 gap-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
{[
|
||||
{ value: '10+', label: language === 'zh' ? 'AI 模型支持' : 'AI Models' },
|
||||
{ value: '5+', label: language === 'zh' ? '交易所集成' : 'Exchanges' },
|
||||
{ value: '24/7', label: language === 'zh' ? '自动交易' : 'Auto Trading' },
|
||||
{ value: '100%', label: language === 'zh' ? '开源免费' : 'Open Source' },
|
||||
].map((stat) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className="text-center p-4 rounded-xl"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.02)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.05)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="text-2xl font-bold mb-1"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-xs" style={{ color: '#5E6673' }}>
|
||||
{stat.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
</AnimatedSection>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,180 +1,169 @@
|
||||
import { Github, Send, ExternalLink } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
import { OFFICIAL_LINKS } from '../../constants/branding'
|
||||
|
||||
interface FooterSectionProps {
|
||||
language: Language
|
||||
}
|
||||
|
||||
export default function FooterSection({ language }: FooterSectionProps) {
|
||||
const links = {
|
||||
social: [
|
||||
{ name: 'GitHub', href: OFFICIAL_LINKS.github, icon: Github },
|
||||
{
|
||||
name: 'X (Twitter)',
|
||||
href: OFFICIAL_LINKS.twitter,
|
||||
icon: () => (
|
||||
<svg viewBox="0 0 24 24" className="w-4 h-4" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{ name: 'Telegram', href: OFFICIAL_LINKS.telegram, icon: Send },
|
||||
],
|
||||
resources: [
|
||||
{
|
||||
name: language === 'zh' ? '文档' : 'Documentation',
|
||||
href: 'https://github.com/NoFxAiOS/nofx/blob/main/README.md',
|
||||
},
|
||||
{ name: 'Issues', href: 'https://github.com/NoFxAiOS/nofx/issues' },
|
||||
{ name: 'Pull Requests', href: 'https://github.com/NoFxAiOS/nofx/pulls' },
|
||||
],
|
||||
supporters: [
|
||||
{ name: 'Aster DEX', href: 'https://www.asterdex.com/en/referral/fdfc0e' },
|
||||
{ name: 'Binance', href: 'https://www.maxweb.red/join?ref=NOFXAI' },
|
||||
{ name: 'Hyperliquid', href: 'https://hyperliquid.xyz/' },
|
||||
{
|
||||
name: 'Amber.ac',
|
||||
href: 'https://amber.ac/',
|
||||
badge: language === 'zh' ? '战略投资' : 'Strategic',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
return (
|
||||
<footer
|
||||
style={{
|
||||
borderTop: '1px solid var(--panel-border)',
|
||||
background: 'var(--brand-dark-gray)',
|
||||
}}
|
||||
>
|
||||
<div className="max-w-[1200px] mx-auto px-6 py-10">
|
||||
{/* Brand */}
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<img src="/icons/nofx.svg" alt="NOFX Logo" className="w-8 h-8" />
|
||||
<div>
|
||||
<div className="text-lg font-bold" style={{ color: '#EAECEF' }}>
|
||||
NOFX
|
||||
<footer style={{ background: '#0B0E11', borderTop: '1px solid rgba(255, 255, 255, 0.06)' }}>
|
||||
<div className="max-w-6xl mx-auto px-4 py-12">
|
||||
{/* Top Section */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-10 mb-12">
|
||||
{/* Brand */}
|
||||
<div className="md:col-span-1">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<img src="/icons/nofx.svg" alt="NOFX Logo" className="w-8 h-8" />
|
||||
<span className="text-xl font-bold" style={{ color: '#EAECEF' }}>
|
||||
NOFX
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs" style={{ color: '#848E9C' }}>
|
||||
<p className="text-sm mb-6" style={{ color: '#5E6673' }}>
|
||||
{t('futureStandardAI', language)}
|
||||
</p>
|
||||
{/* Social Icons */}
|
||||
<div className="flex items-center gap-3">
|
||||
{links.social.map((link) => (
|
||||
<a
|
||||
key={link.name}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-9 h-9 rounded-lg flex items-center justify-center transition-all hover:scale-110"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.05)',
|
||||
color: '#848E9C',
|
||||
}}
|
||||
title={link.name}
|
||||
>
|
||||
<link.icon className="w-4 h-4" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Multi-link columns */}
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-3 gap-8">
|
||||
{/* Links */}
|
||||
<div>
|
||||
<h3
|
||||
className="text-sm font-semibold mb-3"
|
||||
style={{ color: '#EAECEF' }}
|
||||
>
|
||||
<h4 className="text-sm font-semibold mb-4" style={{ color: '#EAECEF' }}>
|
||||
{t('links', language)}
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm" style={{ color: '#848E9C' }}>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://github.com/tinkle-community/nofx"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://t.me/nofx_dev_community"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Telegram
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://x.com/nofx_official"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
X (Twitter)
|
||||
</a>
|
||||
</li>
|
||||
</h4>
|
||||
<ul className="space-y-3">
|
||||
{links.social.map((link) => (
|
||||
<li key={link.name}>
|
||||
<a
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm transition-colors hover:text-[#F0B90B]"
|
||||
style={{ color: '#5E6673' }}
|
||||
>
|
||||
{link.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Resources */}
|
||||
<div>
|
||||
<h3
|
||||
className="text-sm font-semibold mb-3"
|
||||
style={{ color: '#EAECEF' }}
|
||||
>
|
||||
<h4 className="text-sm font-semibold mb-4" style={{ color: '#EAECEF' }}>
|
||||
{t('resources', language)}
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm" style={{ color: '#848E9C' }}>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://github.com/tinkle-community/nofx/blob/main/README.md"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{t('documentation', language)}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://github.com/tinkle-community/nofx/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Issues
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://github.com/tinkle-community/nofx/pulls"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Pull Requests
|
||||
</a>
|
||||
</li>
|
||||
</h4>
|
||||
<ul className="space-y-3">
|
||||
{links.resources.map((link) => (
|
||||
<li key={link.name}>
|
||||
<a
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm transition-colors hover:text-[#F0B90B] inline-flex items-center gap-1"
|
||||
style={{ color: '#5E6673' }}
|
||||
>
|
||||
{link.name}
|
||||
<ExternalLink className="w-3 h-3 opacity-50" />
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Supporters */}
|
||||
<div>
|
||||
<h3
|
||||
className="text-sm font-semibold mb-3"
|
||||
style={{ color: '#EAECEF' }}
|
||||
>
|
||||
<h4 className="text-sm font-semibold mb-4" style={{ color: '#EAECEF' }}>
|
||||
{t('supporters', language)}
|
||||
</h3>
|
||||
<ul className="space-y-2 text-sm" style={{ color: '#848E9C' }}>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://www.asterdex.com/en/referral/fdfc0e"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Aster DEX
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://www.maxweb.red/join?ref=NOFXAI"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Binance
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://hyperliquid.xyz/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Hyperliquid
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="hover:text-[#F0B90B]"
|
||||
href="https://amber.ac/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Amber.ac{' '}
|
||||
<span className="opacity-70">
|
||||
{t('strategicInvestment', language)}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</h4>
|
||||
<ul className="space-y-3">
|
||||
{links.supporters.map((link) => (
|
||||
<li key={link.name}>
|
||||
<a
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm transition-colors hover:text-[#F0B90B] inline-flex items-center gap-2"
|
||||
style={{ color: '#5E6673' }}
|
||||
>
|
||||
{link.name}
|
||||
{link.badge && (
|
||||
<span
|
||||
className="text-xs px-1.5 py-0.5 rounded"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
color: '#F0B90B',
|
||||
}}
|
||||
>
|
||||
{link.badge}
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom note (kept subtle) */}
|
||||
{/* Bottom Section */}
|
||||
<div
|
||||
className="pt-6 mt-8 text-center text-xs"
|
||||
style={{
|
||||
color: 'var(--text-tertiary)',
|
||||
borderTop: '1px solid var(--panel-border)',
|
||||
}}
|
||||
className="pt-6 text-center text-xs"
|
||||
style={{ color: '#5E6673', borderTop: '1px solid rgba(255, 255, 255, 0.06)' }}
|
||||
>
|
||||
<p>{t('footerTitle', language)}</p>
|
||||
<p className="mt-1">{t('footerWarning', language)}</p>
|
||||
<p className="mb-2">{t('footerTitle', language)}</p>
|
||||
<p style={{ color: '#3C4249' }}>{t('footerWarning', language)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
@@ -1,214 +1,252 @@
|
||||
import { motion, useScroll, useTransform, useAnimation } from 'framer-motion'
|
||||
import { Sparkles } from 'lucide-react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowRight, Play, Github, Zap } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
import { useGitHubStats } from '../../hooks/useGitHubStats'
|
||||
import { useCounterAnimation } from '../../hooks/useCounterAnimation'
|
||||
import { OFFICIAL_LINKS } from '../../constants/branding'
|
||||
|
||||
interface HeroSectionProps {
|
||||
language: Language
|
||||
}
|
||||
|
||||
export default function HeroSection({ language }: HeroSectionProps) {
|
||||
const { scrollYProgress } = useScroll()
|
||||
const opacity = useTransform(scrollYProgress, [0, 0.2], [1, 0])
|
||||
const scale = useTransform(scrollYProgress, [0, 0.2], [1, 0.8])
|
||||
const handControls = useAnimation()
|
||||
const { stars, daysOld, isLoading } = useGitHubStats('NoFxAiOS', 'nofx')
|
||||
|
||||
// 动画数字 - 仅对 stars 添加动画
|
||||
const animatedStars = useCounterAnimation({
|
||||
start: 0,
|
||||
end: stars,
|
||||
duration: 2000,
|
||||
})
|
||||
|
||||
const fadeInUp = {
|
||||
initial: { opacity: 0, y: 60 },
|
||||
animate: { opacity: 1, y: 0 },
|
||||
transition: { duration: 0.6, ease: [0.6, -0.05, 0.01, 0.99] },
|
||||
}
|
||||
const staggerContainer = {
|
||||
animate: { transition: { staggerChildren: 0.1 } },
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="relative pt-32 pb-20 px-4">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
||||
{/* Left Content */}
|
||||
<motion.div
|
||||
className="space-y-6 relative z-10"
|
||||
style={{ opacity, scale }}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
variants={staggerContainer}
|
||||
>
|
||||
<motion.div variants={fadeInUp}>
|
||||
<motion.div
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full mb-6"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
||||
}}
|
||||
whileHover={{
|
||||
scale: 1.05,
|
||||
boxShadow: '0 0 20px rgba(240, 185, 11, 0.2)',
|
||||
}}
|
||||
>
|
||||
<Sparkles
|
||||
className="w-4 h-4"
|
||||
style={{ color: 'var(--brand-yellow)' }}
|
||||
/>
|
||||
<span
|
||||
className="text-sm font-semibold"
|
||||
style={{ color: 'var(--brand-yellow)' }}
|
||||
>
|
||||
{isLoading ? (
|
||||
t('githubStarsInDays', language)
|
||||
) : language === 'zh' ? (
|
||||
<>
|
||||
{daysOld} 天内{' '}
|
||||
<span className="inline-block tabular-nums">
|
||||
{(animatedStars / 1000).toFixed(1)}
|
||||
</span>
|
||||
K+ GitHub Stars
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="inline-block tabular-nums">
|
||||
{(animatedStars / 1000).toFixed(1)}
|
||||
</span>
|
||||
K+ GitHub Stars in {daysOld} days
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
<h1
|
||||
className="text-5xl lg:text-7xl font-bold leading-tight"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
>
|
||||
{t('heroTitle1', language)}
|
||||
<br />
|
||||
<span style={{ color: 'var(--brand-yellow)' }}>
|
||||
{t('heroTitle2', language)}
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
<motion.p
|
||||
className="text-xl leading-relaxed"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
variants={fadeInUp}
|
||||
>
|
||||
{t('heroDescription', language)}
|
||||
</motion.p>
|
||||
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<motion.a
|
||||
href="https://github.com/tinkle-community/nofx"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
transition={{ type: 'spring', stiffness: 400 }}
|
||||
>
|
||||
<img
|
||||
src="https://img.shields.io/github/stars/tinkle-community/nofx?style=for-the-badge&logo=github&logoColor=white&color=F0B90B&labelColor=0A0A0A"
|
||||
alt="GitHub Stars"
|
||||
className="h-7"
|
||||
/>
|
||||
</motion.a>
|
||||
<motion.a
|
||||
href="https://github.com/tinkle-community/nofx/network/members"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
transition={{ type: 'spring', stiffness: 400 }}
|
||||
>
|
||||
<img
|
||||
src="https://img.shields.io/github/forks/tinkle-community/nofx?style=for-the-badge&logo=github&logoColor=white&color=F0B90B&labelColor=0A0A0A"
|
||||
alt="GitHub Forks"
|
||||
className="h-7"
|
||||
/>
|
||||
</motion.a>
|
||||
<motion.a
|
||||
href="https://github.com/tinkle-community/nofx/graphs/contributors"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
whileHover={{ scale: 1.05 }}
|
||||
transition={{ type: 'spring', stiffness: 400 }}
|
||||
>
|
||||
<img
|
||||
src="https://img.shields.io/github/contributors/tinkle-community/nofx?style=for-the-badge&logo=github&logoColor=white&color=F0B90B&labelColor=0A0A0A"
|
||||
alt="GitHub Contributors"
|
||||
className="h-7"
|
||||
/>
|
||||
</motion.a>
|
||||
</div>
|
||||
|
||||
<motion.p
|
||||
className="text-xs pt-4"
|
||||
style={{ color: 'var(--text-tertiary)' }}
|
||||
variants={fadeInUp}
|
||||
>
|
||||
{t('poweredBy', language)}
|
||||
</motion.p>
|
||||
</motion.div>
|
||||
|
||||
{/* Right Visual - Interactive Robot */}
|
||||
<div
|
||||
className="relative w-full cursor-pointer"
|
||||
onMouseEnter={() => {
|
||||
handControls.start({
|
||||
y: [-8, 8, -8],
|
||||
rotate: [-3, 3, -3],
|
||||
x: [-2, 2, -2],
|
||||
transition: {
|
||||
duration: 2.5,
|
||||
repeat: Infinity,
|
||||
ease: 'easeInOut',
|
||||
times: [0, 0.5, 1],
|
||||
},
|
||||
})
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
handControls.start({
|
||||
y: 0,
|
||||
rotate: 0,
|
||||
x: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: 'easeOut',
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
{/* Background Layer */}
|
||||
<motion.img
|
||||
src="/images/hand-bg.png"
|
||||
alt="NOFX Platform Background"
|
||||
className="w-full opacity-90"
|
||||
style={{ opacity, scale }}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
transition={{ type: 'spring', stiffness: 300 }}
|
||||
/>
|
||||
|
||||
{/* Hand Layer - Animated */}
|
||||
<motion.img
|
||||
src="/images/hand.png"
|
||||
alt="Robot Hand"
|
||||
className="absolute top-0 left-0 w-full"
|
||||
style={{ opacity }}
|
||||
animate={handControls}
|
||||
initial={{ y: 0, rotate: 0, x: 0 }}
|
||||
whileHover={{
|
||||
scale: 1.05,
|
||||
transition: { type: 'spring', stiffness: 400 },
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<section className="relative min-h-screen flex items-center justify-center overflow-hidden pt-16">
|
||||
{/* Animated Background */}
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
{/* Grid Pattern */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.03]"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(#F0B90B 1px, transparent 1px), linear-gradient(90deg, #F0B90B 1px, transparent 1px)`,
|
||||
backgroundSize: '60px 60px',
|
||||
}}
|
||||
/>
|
||||
{/* Radial Gradient */}
|
||||
<div
|
||||
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] rounded-full"
|
||||
style={{
|
||||
background: 'radial-gradient(circle, rgba(240, 185, 11, 0.08) 0%, transparent 70%)',
|
||||
}}
|
||||
/>
|
||||
{/* Floating Orbs */}
|
||||
<motion.div
|
||||
className="absolute top-20 right-20 w-32 h-32 rounded-full blur-3xl"
|
||||
style={{ background: 'rgba(240, 185, 11, 0.15)' }}
|
||||
animate={{
|
||||
y: [0, 30, 0],
|
||||
scale: [1, 1.1, 1],
|
||||
}}
|
||||
transition={{ duration: 8, repeat: Infinity, ease: 'easeInOut' }}
|
||||
/>
|
||||
<motion.div
|
||||
className="absolute bottom-40 left-20 w-48 h-48 rounded-full blur-3xl"
|
||||
style={{ background: 'rgba(240, 185, 11, 0.1)' }}
|
||||
animate={{
|
||||
y: [0, -40, 0],
|
||||
scale: [1, 1.2, 1],
|
||||
}}
|
||||
transition={{ duration: 10, repeat: Infinity, ease: 'easeInOut' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 max-w-6xl mx-auto px-4 text-center">
|
||||
{/* Badge */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full mb-8"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.1)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
>
|
||||
<Zap className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-sm font-medium" style={{ color: '#F0B90B' }}>
|
||||
{isLoading ? (
|
||||
t('githubStarsInDays', language)
|
||||
) : language === 'zh' ? (
|
||||
<>
|
||||
{daysOld} 天内获得{' '}
|
||||
<span className="font-bold tabular-nums">
|
||||
{(animatedStars / 1000).toFixed(1)}K+
|
||||
</span>{' '}
|
||||
GitHub Stars
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span className="font-bold tabular-nums">
|
||||
{(animatedStars / 1000).toFixed(1)}K+
|
||||
</span>{' '}
|
||||
GitHub Stars in {daysOld} days
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
{/* Main Title */}
|
||||
<motion.h1
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.1 }}
|
||||
className="text-5xl sm:text-6xl lg:text-7xl font-bold mb-6 leading-tight"
|
||||
>
|
||||
<span style={{ color: '#EAECEF' }}>{t('heroTitle1', language)}</span>
|
||||
<br />
|
||||
<span
|
||||
className="relative inline-block"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{t('heroTitle2', language)}
|
||||
<motion.span
|
||||
className="absolute -bottom-2 left-0 h-1 rounded-full"
|
||||
style={{ background: 'linear-gradient(90deg, #F0B90B, #FCD535)' }}
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: '100%' }}
|
||||
transition={{ duration: 0.8, delay: 0.8 }}
|
||||
/>
|
||||
</span>
|
||||
</motion.h1>
|
||||
|
||||
{/* Description */}
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="text-lg sm:text-xl max-w-3xl mx-auto mb-10 leading-relaxed"
|
||||
style={{ color: '#848E9C' }}
|
||||
>
|
||||
{t('heroDescription', language)}
|
||||
</motion.p>
|
||||
|
||||
{/* CTA Buttons */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
className="flex flex-col sm:flex-row items-center justify-center gap-4 mb-12"
|
||||
>
|
||||
<motion.a
|
||||
href="/competition"
|
||||
className="group flex items-center gap-3 px-8 py-4 rounded-xl font-bold text-lg transition-all"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||
color: '#0B0E11',
|
||||
boxShadow: '0 4px 24px rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
whileHover={{
|
||||
scale: 1.02,
|
||||
boxShadow: '0 8px 32px rgba(240, 185, 11, 0.4)',
|
||||
}}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
<Play className="w-5 h-5" />
|
||||
{t('liveCompetition', language) || 'Live Competition'}
|
||||
<ArrowRight className="w-5 h-5 transition-transform group-hover:translate-x-1" />
|
||||
</motion.a>
|
||||
|
||||
<motion.a
|
||||
href={OFFICIAL_LINKS.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group flex items-center gap-3 px-8 py-4 rounded-xl font-bold text-lg transition-all"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.05)',
|
||||
color: '#EAECEF',
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
}}
|
||||
whileHover={{
|
||||
scale: 1.02,
|
||||
background: 'rgba(255, 255, 255, 0.08)',
|
||||
borderColor: 'rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
<Github className="w-5 h-5" />
|
||||
{t('viewSourceCode', language)}
|
||||
</motion.a>
|
||||
</motion.div>
|
||||
|
||||
{/* Stats Row */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
className="flex flex-wrap items-center justify-center gap-8 sm:gap-12"
|
||||
>
|
||||
{[
|
||||
{ label: 'GitHub Stars', value: `${(stars / 1000).toFixed(1)}K+` },
|
||||
{ label: language === 'zh' ? '支持交易所' : 'Exchanges', value: '5+' },
|
||||
{ label: language === 'zh' ? 'AI 模型' : 'AI Models', value: '10+' },
|
||||
{ label: language === 'zh' ? '开源免费' : 'Open Source', value: '100%' },
|
||||
].map((stat, index) => (
|
||||
<motion.div
|
||||
key={stat.label}
|
||||
className="text-center"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.5 + index * 0.1 }}
|
||||
>
|
||||
<div
|
||||
className="text-3xl sm:text-4xl font-bold mb-1"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||
WebkitBackgroundClip: 'text',
|
||||
WebkitTextFillColor: 'transparent',
|
||||
}}
|
||||
>
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-sm" style={{ color: '#5E6673' }}>
|
||||
{stat.label}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* Powered By */}
|
||||
<motion.p
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.8 }}
|
||||
className="mt-16 text-xs"
|
||||
style={{ color: '#5E6673' }}
|
||||
>
|
||||
{t('poweredBy', language)}
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator */}
|
||||
<motion.div
|
||||
className="absolute bottom-8 left-1/2 -translate-x-1/2"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 1 }}
|
||||
>
|
||||
<motion.div
|
||||
className="w-6 h-10 rounded-full flex justify-center pt-2"
|
||||
style={{ border: '2px solid rgba(240, 185, 11, 0.3)' }}
|
||||
animate={{ y: [0, 8, 0] }}
|
||||
transition={{ duration: 2, repeat: Infinity }}
|
||||
>
|
||||
<motion.div
|
||||
className="w-1.5 h-3 rounded-full"
|
||||
style={{ background: '#F0B90B' }}
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,140 +1,167 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import AnimatedSection from './AnimatedSection'
|
||||
import { Download, Rocket, TrendingUp, AlertTriangle } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
|
||||
function StepCard({ number, title, description, delay }: any) {
|
||||
return (
|
||||
<motion.div
|
||||
className="flex gap-6 items-start"
|
||||
initial={{ opacity: 0, x: -50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay }}
|
||||
whileHover={{ x: 10 }}
|
||||
>
|
||||
<motion.div
|
||||
className="flex-shrink-0 w-14 h-14 rounded-full flex items-center justify-center font-bold text-2xl"
|
||||
style={{
|
||||
background: 'var(--binance-yellow)',
|
||||
color: 'var(--brand-black)',
|
||||
}}
|
||||
whileHover={{ scale: 1.2, rotate: 360 }}
|
||||
transition={{ type: 'spring', stiffness: 260, damping: 20 }}
|
||||
>
|
||||
{number}
|
||||
</motion.div>
|
||||
<div>
|
||||
<h3
|
||||
className="text-2xl font-semibold mb-2"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
<p
|
||||
className="text-lg leading-relaxed"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
|
||||
interface HowItWorksSectionProps {
|
||||
language: Language
|
||||
}
|
||||
|
||||
export default function HowItWorksSection({
|
||||
language,
|
||||
}: HowItWorksSectionProps) {
|
||||
export default function HowItWorksSection({ language }: HowItWorksSectionProps) {
|
||||
const steps = [
|
||||
{
|
||||
icon: Download,
|
||||
number: '01',
|
||||
title: language === 'zh' ? '克隆项目' : 'Clone Project',
|
||||
desc: language === 'zh'
|
||||
? 'git clone 项目到本地'
|
||||
: 'git clone the project locally',
|
||||
code: 'git clone https://github.com/NoFxAiOS/nofx.git',
|
||||
},
|
||||
{
|
||||
icon: Rocket,
|
||||
number: '02',
|
||||
title: language === 'zh' ? '启动服务' : 'Start Service',
|
||||
desc: language === 'zh'
|
||||
? 'Docker 一键启动所有服务'
|
||||
: 'Docker one-click start all services',
|
||||
code: './start.sh start --build',
|
||||
},
|
||||
{
|
||||
icon: TrendingUp,
|
||||
number: '03',
|
||||
title: language === 'zh' ? '开始交易' : 'Start Trading',
|
||||
desc: language === 'zh'
|
||||
? '创建交易员,让 AI 开始工作'
|
||||
: 'Create trader, let AI do the work',
|
||||
code: 'http://localhost:3000',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<AnimatedSection id="how-it-works" backgroundColor="var(--brand-dark-gray)">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<section className="py-24 relative overflow-hidden" style={{ background: '#0D1117' }}>
|
||||
{/* Background Decoration */}
|
||||
<div
|
||||
className="absolute left-0 top-1/2 -translate-y-1/2 w-96 h-96 rounded-full blur-3xl opacity-20"
|
||||
style={{ background: 'radial-gradient(circle, rgba(240, 185, 11, 0.15) 0%, transparent 70%)' }}
|
||||
/>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-4 relative z-10">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
className="text-center mb-16"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2
|
||||
className="text-4xl font-bold mb-4"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
>
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#EAECEF' }}>
|
||||
{t('howToStart', language)}
|
||||
</h2>
|
||||
<p className="text-lg" style={{ color: 'var(--text-secondary)' }}>
|
||||
<p className="text-lg" style={{ color: '#848E9C' }}>
|
||||
{t('fourSimpleSteps', language)}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="space-y-8">
|
||||
{[
|
||||
{
|
||||
number: 1,
|
||||
title: t('step1Title', language),
|
||||
description: t('step1Desc', language),
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
title: t('step2Title', language),
|
||||
description: t('step2Desc', language),
|
||||
},
|
||||
{
|
||||
number: 3,
|
||||
title: t('step3Title', language),
|
||||
description: t('step3Desc', language),
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
title: t('step4Title', language),
|
||||
description: t('step4Desc', language),
|
||||
},
|
||||
].map((step, index) => (
|
||||
<StepCard key={step.number} {...step} delay={index * 0.1} />
|
||||
))}
|
||||
{/* Steps Timeline */}
|
||||
<div className="relative">
|
||||
{/* Connecting Line */}
|
||||
<div
|
||||
className="absolute left-[39px] top-0 bottom-0 w-px hidden lg:block"
|
||||
style={{ background: 'linear-gradient(to bottom, transparent, rgba(240, 185, 11, 0.3), transparent)' }}
|
||||
/>
|
||||
|
||||
<div className="space-y-6">
|
||||
{steps.map((step, index) => (
|
||||
<motion.div
|
||||
key={step.number}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.15 }}
|
||||
className="relative"
|
||||
>
|
||||
<div
|
||||
className="flex flex-col lg:flex-row items-start gap-6 p-6 rounded-2xl transition-all duration-300 hover:translate-x-2"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.02)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.05)',
|
||||
}}
|
||||
>
|
||||
{/* Number Circle */}
|
||||
<div className="flex-shrink-0 relative z-10">
|
||||
<motion.div
|
||||
className="w-20 h-20 rounded-2xl flex items-center justify-center"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, rgba(240, 185, 11, 0.2) 0%, rgba(240, 185, 11, 0.05) 100%)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
>
|
||||
<step.icon className="w-8 h-8" style={{ color: '#F0B90B' }} />
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-grow">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<span
|
||||
className="text-sm font-mono font-bold"
|
||||
style={{ color: '#F0B90B' }}
|
||||
>
|
||||
{step.number}
|
||||
</span>
|
||||
<h3 className="text-xl font-bold" style={{ color: '#EAECEF' }}>
|
||||
{step.title}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-4" style={{ color: '#848E9C' }}>
|
||||
{step.desc}
|
||||
</p>
|
||||
|
||||
{/* Code Block */}
|
||||
<div
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg font-mono text-sm"
|
||||
style={{
|
||||
background: 'rgba(0, 0, 0, 0.3)',
|
||||
border: '1px solid rgba(255, 255, 255, 0.06)',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#5E6673' }}>$</span>
|
||||
<span style={{ color: '#EAECEF' }}>{step.code}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Risk Warning */}
|
||||
<motion.div
|
||||
className="mt-12 p-6 rounded-xl flex items-start gap-4"
|
||||
className="mt-12 p-6 rounded-2xl flex items-start gap-4"
|
||||
style={{
|
||||
background: 'rgba(246, 70, 93, 0.1)',
|
||||
border: '1px solid rgba(246, 70, 93, 0.3)',
|
||||
background: 'rgba(240, 185, 11, 0.05)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.15)',
|
||||
}}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
>
|
||||
<div
|
||||
className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0"
|
||||
style={{ background: 'rgba(246, 70, 93, 0.2)', color: '#F6465D' }}
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
|
||||
style={{ background: 'rgba(240, 185, 11, 0.1)' }}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-6 h-6"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" />
|
||||
<line x1="12" x2="12" y1="9" y2="13" />
|
||||
<line x1="12" x2="12.01" y1="17" y2="17" />
|
||||
</svg>
|
||||
<AlertTriangle className="w-6 h-6" style={{ color: '#F0B90B' }} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-semibold mb-2" style={{ color: '#F6465D' }}>
|
||||
<div className="font-semibold mb-2" style={{ color: '#F0B90B' }}>
|
||||
{t('importantRiskWarning', language)}
|
||||
</div>
|
||||
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
||||
<p className="text-sm leading-relaxed" style={{ color: '#5E6673' }}>
|
||||
{t('riskWarningText', language)}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</AnimatedSection>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
WebCryptoEnvironmentCheck,
|
||||
type WebCryptoCheckStatus,
|
||||
} from '../WebCryptoEnvironmentCheck'
|
||||
import { BookOpen, Trash2, HelpCircle } from 'lucide-react'
|
||||
import { BookOpen, Trash2, HelpCircle, ExternalLink, UserPlus } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { Tooltip } from './Tooltip'
|
||||
import { getShortName } from './utils'
|
||||
@@ -92,6 +92,16 @@ export function ExchangeConfigModal({
|
||||
(e) => e.id === selectedExchangeId
|
||||
)
|
||||
|
||||
// 交易所注册链接配置
|
||||
const exchangeRegistrationLinks: Record<string, { url: string; hasReferral?: boolean }> = {
|
||||
binance: { url: 'https://www.binance.com/join?ref=NOFXENG', hasReferral: true },
|
||||
okx: { url: 'https://www.okx.com/join/1865360', hasReferral: true },
|
||||
bybit: { url: 'https://partner.bybit.com/b/83856', hasReferral: true },
|
||||
hyperliquid: { url: 'https://app.hyperliquid.xyz/join/AITRADING', hasReferral: true },
|
||||
aster: { url: 'https://www.asterdex.com/en/referral/fdfc0e', hasReferral: true },
|
||||
lighter: { url: 'https://lighter.xyz', hasReferral: false },
|
||||
}
|
||||
|
||||
// 如果是编辑现有交易所,初始化表单数据
|
||||
useEffect(() => {
|
||||
if (editingExchangeId && selectedExchange) {
|
||||
@@ -411,6 +421,34 @@ export function ExchangeConfigModal({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 注册链接 */}
|
||||
<a
|
||||
href={exchangeRegistrationLinks[selectedExchange.id]?.url || '#'}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-between p-3 rounded-lg mt-3 transition-all hover:scale-[1.02]"
|
||||
style={{
|
||||
background: 'rgba(240, 185, 11, 0.08)',
|
||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<UserPlus className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||
<span className="text-sm" style={{ color: '#EAECEF' }}>
|
||||
{language === 'zh' ? '还没有交易所账号?点击注册' : "No exchange account? Register here"}
|
||||
</span>
|
||||
{exchangeRegistrationLinks[selectedExchange.id]?.hasReferral && (
|
||||
<span
|
||||
className="text-xs px-1.5 py-0.5 rounded"
|
||||
style={{ background: 'rgba(14, 203, 129, 0.2)', color: '#0ECB81' }}
|
||||
>
|
||||
{language === 'zh' ? '折扣优惠' : 'Discount'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<ExternalLink className="w-4 h-4" style={{ color: '#848E9C' }} />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { useState } from 'react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowRight } from 'lucide-react'
|
||||
import { ArrowRight, Github } from 'lucide-react'
|
||||
import HeaderBar from '../components/HeaderBar'
|
||||
import HeroSection from '../components/landing/HeroSection'
|
||||
import AboutSection from '../components/landing/AboutSection'
|
||||
import FeaturesSection from '../components/landing/FeaturesSection'
|
||||
import HowItWorksSection from '../components/landing/HowItWorksSection'
|
||||
import CommunitySection from '../components/landing/CommunitySection'
|
||||
import AnimatedSection from '../components/landing/AnimatedSection'
|
||||
import LoginModal from '../components/landing/LoginModal'
|
||||
import FooterSection from '../components/landing/FooterSection'
|
||||
import { useAuth } from '../contexts/AuthContext'
|
||||
import { useLanguage } from '../contexts/LanguageContext'
|
||||
import { t } from '../i18n/translations'
|
||||
import { OFFICIAL_LINKS } from '../constants/branding'
|
||||
|
||||
export function LandingPage() {
|
||||
const [showLoginModal, setShowLoginModal] = useState(false)
|
||||
@@ -20,7 +20,6 @@ export function LandingPage() {
|
||||
const { language, setLanguage } = useLanguage()
|
||||
const isLoggedIn = !!user
|
||||
|
||||
console.log('LandingPage - user:', user, 'isLoggedIn:', isLoggedIn)
|
||||
return (
|
||||
<>
|
||||
<HeaderBar
|
||||
@@ -32,7 +31,6 @@ export function LandingPage() {
|
||||
user={user}
|
||||
onLogout={logout}
|
||||
onPageChange={(page) => {
|
||||
console.log('LandingPage onPageChange called with:', page)
|
||||
if (page === 'competition') {
|
||||
window.location.href = '/competition'
|
||||
} else if (page === 'traders') {
|
||||
@@ -43,24 +41,30 @@ export function LandingPage() {
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="min-h-screen px-4 sm:px-6 lg:px-8"
|
||||
className="min-h-screen"
|
||||
style={{
|
||||
background: 'var(--brand-black)',
|
||||
color: 'var(--brand-light-gray)',
|
||||
background: '#0B0E11',
|
||||
color: '#EAECEF',
|
||||
}}
|
||||
>
|
||||
<HeroSection language={language} />
|
||||
<AboutSection language={language} />
|
||||
<FeaturesSection language={language} />
|
||||
<HowItWorksSection language={language} />
|
||||
<CommunitySection />
|
||||
<CommunitySection language={language} />
|
||||
|
||||
{/* CTA */}
|
||||
<AnimatedSection backgroundColor="var(--panel-bg)">
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
{/* Final CTA Section */}
|
||||
<section className="py-24 relative overflow-hidden" style={{ background: '#0D1117' }}>
|
||||
{/* Background Glow */}
|
||||
<div
|
||||
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[600px] h-[600px] rounded-full blur-3xl opacity-30"
|
||||
style={{ background: 'radial-gradient(circle, rgba(240, 185, 11, 0.15) 0%, transparent 70%)' }}
|
||||
/>
|
||||
|
||||
<div className="max-w-4xl mx-auto px-4 text-center relative z-10">
|
||||
<motion.h2
|
||||
className="text-5xl font-bold mb-6"
|
||||
style={{ color: 'var(--brand-light-gray)' }}
|
||||
className="text-4xl lg:text-5xl font-bold mb-6"
|
||||
style={{ color: '#EAECEF' }}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
@@ -68,8 +72,8 @@ export function LandingPage() {
|
||||
{t('readyToDefine', language)}
|
||||
</motion.h2>
|
||||
<motion.p
|
||||
className="text-xl mb-12"
|
||||
style={{ color: 'var(--text-secondary)' }}
|
||||
className="text-lg mb-10 max-w-2xl mx-auto"
|
||||
style={{ color: '#848E9C' }}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
@@ -77,46 +81,55 @@ export function LandingPage() {
|
||||
>
|
||||
{t('startWithCrypto', language)}
|
||||
</motion.p>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
|
||||
<motion.div
|
||||
className="flex flex-col sm:flex-row items-center justify-center gap-4"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.2 }}
|
||||
>
|
||||
<motion.button
|
||||
onClick={() => setShowLoginModal(true)}
|
||||
className="flex items-center gap-2 px-10 py-4 rounded-lg font-semibold text-lg"
|
||||
className="group flex items-center gap-3 px-8 py-4 rounded-xl font-bold text-lg"
|
||||
style={{
|
||||
background: 'var(--brand-yellow)',
|
||||
color: 'var(--brand-black)',
|
||||
}}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
{t('getStartedNow', language)}
|
||||
<motion.div
|
||||
animate={{ x: [0, 5, 0] }}
|
||||
transition={{ duration: 1.5, repeat: Infinity }}
|
||||
>
|
||||
<ArrowRight className="w-5 h-5" />
|
||||
</motion.div>
|
||||
</motion.button>
|
||||
<motion.a
|
||||
href="https://github.com/tinkle-community/nofx/tree/dev"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-2 px-10 py-4 rounded-lg font-semibold text-lg"
|
||||
style={{
|
||||
background: 'transparent',
|
||||
color: 'var(--brand-light-gray)',
|
||||
border: '2px solid var(--brand-yellow)',
|
||||
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||
color: '#0B0E11',
|
||||
boxShadow: '0 4px 24px rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
whileHover={{
|
||||
scale: 1.05,
|
||||
backgroundColor: 'rgba(240, 185, 11, 0.1)',
|
||||
scale: 1.02,
|
||||
boxShadow: '0 8px 32px rgba(240, 185, 11, 0.4)',
|
||||
}}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
{t('getStartedNow', language)}
|
||||
<ArrowRight className="w-5 h-5 transition-transform group-hover:translate-x-1" />
|
||||
</motion.button>
|
||||
|
||||
<motion.a
|
||||
href={OFFICIAL_LINKS.github}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="group flex items-center gap-3 px-8 py-4 rounded-xl font-bold text-lg"
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.05)',
|
||||
color: '#EAECEF',
|
||||
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||
}}
|
||||
whileHover={{
|
||||
scale: 1.02,
|
||||
background: 'rgba(255, 255, 255, 0.08)',
|
||||
borderColor: 'rgba(240, 185, 11, 0.3)',
|
||||
}}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
<Github className="w-5 h-5" />
|
||||
{t('viewSourceCode', language)}
|
||||
</motion.a>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</AnimatedSection>
|
||||
</section>
|
||||
|
||||
{showLoginModal && (
|
||||
<LoginModal
|
||||
|
||||
Reference in New Issue
Block a user