mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-15 00:36:56 +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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes trader
|
// Delete deletes trader and associated data
|
||||||
func (s *TraderStore) Delete(userID, id string) error {
|
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)
|
_, err := s.db.Exec(`DELETE FROM traders WHERE id = ? AND user_id = ?`, id, userID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import {
|
|||||||
BookOpen,
|
BookOpen,
|
||||||
HelpCircle,
|
HelpCircle,
|
||||||
Pencil,
|
Pencil,
|
||||||
|
UserPlus,
|
||||||
|
ExternalLink,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
import { confirmToast } from '../lib/notify'
|
import { confirmToast } from '../lib/notify'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
@@ -1611,6 +1613,16 @@ function ExchangeConfigModal({
|
|||||||
(e) => e.id === selectedExchangeId
|
(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(() => {
|
useEffect(() => {
|
||||||
if (editingExchangeId && selectedExchange) {
|
if (editingExchangeId && selectedExchange) {
|
||||||
@@ -1918,6 +1930,35 @@ function ExchangeConfigModal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { Menu, X, ChevronDown } from 'lucide-react'
|
|||||||
import { t, type Language } from '../i18n/translations'
|
import { t, type Language } from '../i18n/translations'
|
||||||
import { Container } from './Container'
|
import { Container } from './Container'
|
||||||
import { useSystemConfig } from '../hooks/useSystemConfig'
|
import { useSystemConfig } from '../hooks/useSystemConfig'
|
||||||
|
import { OFFICIAL_LINKS } from '../constants/branding'
|
||||||
|
|
||||||
type Page =
|
type Page =
|
||||||
| 'competition'
|
| 'competition'
|
||||||
@@ -430,45 +431,77 @@ export default function HeaderBar({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Side - Original Navigation Items and Login */}
|
{/* Right Side - Social Links and User Actions */}
|
||||||
<div className="flex items-center gap-6">
|
<div className="flex items-center gap-4">
|
||||||
{/* Only show original navigation items on home page */}
|
{/* Social Links - Always visible */}
|
||||||
{isHomePage &&
|
<div className="flex items-center gap-1">
|
||||||
[
|
{/* GitHub */}
|
||||||
{ key: 'features', label: t('features', language) },
|
<a
|
||||||
{ key: 'howItWorks', label: t('howItWorks', language) },
|
href={OFFICIAL_LINKS.github}
|
||||||
{ key: 'GitHub', label: 'GitHub' },
|
target="_blank"
|
||||||
{ key: 'community', label: t('community', language) },
|
rel="noopener noreferrer"
|
||||||
].map((item) => (
|
className="p-2 rounded-lg transition-all hover:scale-110"
|
||||||
<a
|
style={{ color: '#848E9C' }}
|
||||||
key={item.key}
|
onMouseEnter={(e) => {
|
||||||
href={
|
e.currentTarget.style.color = '#EAECEF'
|
||||||
item.key === 'GitHub'
|
e.currentTarget.style.background = 'rgba(255, 255, 255, 0.05)'
|
||||||
? 'https://github.com/tinkle-community/nofx'
|
}}
|
||||||
: item.key === 'community'
|
onMouseLeave={(e) => {
|
||||||
? 'https://t.me/nofx_dev_community'
|
e.currentTarget.style.color = '#848E9C'
|
||||||
: `#${item.key === 'features' ? 'features' : 'how-it-works'}`
|
e.currentTarget.style.background = 'transparent'
|
||||||
}
|
}}
|
||||||
target={
|
title="GitHub"
|
||||||
item.key === 'GitHub' || item.key === 'community'
|
>
|
||||||
? '_blank'
|
<svg width="18" height="18" viewBox="0 0 16 16" fill="currentColor">
|
||||||
: undefined
|
<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>
|
||||||
rel={
|
</a>
|
||||||
item.key === 'GitHub' || item.key === 'community'
|
{/* Twitter/X */}
|
||||||
? 'noopener noreferrer'
|
<a
|
||||||
: undefined
|
href={OFFICIAL_LINKS.twitter}
|
||||||
}
|
target="_blank"
|
||||||
className="text-sm transition-colors relative group"
|
rel="noopener noreferrer"
|
||||||
style={{ color: 'var(--brand-light-gray)' }}
|
className="p-2 rounded-lg transition-all hover:scale-110"
|
||||||
>
|
style={{ color: '#848E9C' }}
|
||||||
{item.label}
|
onMouseEnter={(e) => {
|
||||||
<span
|
e.currentTarget.style.color = '#1DA1F2'
|
||||||
className="absolute -bottom-1 left-0 w-0 h-0.5 group-hover:w-full transition-all duration-300"
|
e.currentTarget.style.background = 'rgba(29, 161, 242, 0.1)'
|
||||||
style={{ background: 'var(--brand-yellow)' }}
|
}}
|
||||||
/>
|
onMouseLeave={(e) => {
|
||||||
</a>
|
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 */}
|
{/* User Info and Actions */}
|
||||||
{isLoggedIn && user ? (
|
{isLoggedIn && user ? (
|
||||||
@@ -932,28 +965,10 @@ export default function HeaderBar({
|
|||||||
[
|
[
|
||||||
{ key: 'features', label: t('features', language) },
|
{ key: 'features', label: t('features', language) },
|
||||||
{ key: 'howItWorks', label: t('howItWorks', language) },
|
{ key: 'howItWorks', label: t('howItWorks', language) },
|
||||||
{ key: 'GitHub', label: 'GitHub' },
|
|
||||||
{ key: 'community', label: t('community', language) },
|
|
||||||
].map((item) => (
|
].map((item) => (
|
||||||
<a
|
<a
|
||||||
key={item.key}
|
key={item.key}
|
||||||
href={
|
href={`#${item.key === 'features' ? 'features' : 'how-it-works'}`}
|
||||||
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="block text-sm py-2"
|
className="block text-sm py-2"
|
||||||
style={{ color: 'var(--brand-light-gray)' }}
|
style={{ color: 'var(--brand-light-gray)' }}
|
||||||
>
|
>
|
||||||
@@ -961,6 +976,43 @@ export default function HeaderBar({
|
|||||||
</a>
|
</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 */}
|
{/* Language Toggle */}
|
||||||
<div className="py-2">
|
<div className="py-2">
|
||||||
<div className="flex items-center gap-2 mb-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 { useLanguage } from '../contexts/LanguageContext'
|
||||||
import { t } from '../i18n/translations'
|
import { t } from '../i18n/translations'
|
||||||
import { toast } from 'sonner'
|
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'
|
import { httpClient } from '../lib/httpClient'
|
||||||
|
|
||||||
// 提取下划线后面的名称部分
|
// 提取下划线后面的名称部分
|
||||||
@@ -12,6 +12,16 @@ function getShortName(fullName: string): string {
|
|||||||
return parts.length > 1 ? parts[parts.length - 1] : fullName
|
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'
|
import type { TraderConfigData } from '../types'
|
||||||
|
|
||||||
// 表单内部状态类型
|
// 表单内部状态类型
|
||||||
@@ -272,6 +282,30 @@ export function TraderConfigModal({
|
|||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { Shield, Target } from 'lucide-react'
|
import { Terminal, Shield, Cpu, BarChart3 } from 'lucide-react'
|
||||||
import AnimatedSection from './AnimatedSection'
|
|
||||||
import Typewriter from '../Typewriter'
|
|
||||||
import { t, Language } from '../../i18n/translations'
|
import { t, Language } from '../../i18n/translations'
|
||||||
|
|
||||||
interface AboutSectionProps {
|
interface AboutSectionProps {
|
||||||
@@ -9,119 +7,151 @@ interface AboutSectionProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AboutSection({ language }: 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 (
|
return (
|
||||||
<AnimatedSection id="about" backgroundColor="var(--brand-dark-gray)">
|
<section className="py-24 relative overflow-hidden" style={{ background: '#0B0E11' }}>
|
||||||
<div className="max-w-7xl mx-auto">
|
{/* Background Decoration */}
|
||||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
<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
|
<motion.div
|
||||||
className="space-y-6"
|
initial={{ opacity: 0, x: -30 }}
|
||||||
initial={{ opacity: 0, x: -50 }}
|
|
||||||
whileInView={{ opacity: 1, x: 0 }}
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ duration: 0.6 }}
|
transition={{ duration: 0.6 }}
|
||||||
>
|
>
|
||||||
<motion.div
|
<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={{
|
style={{
|
||||||
background: 'rgba(240, 185, 11, 0.1)',
|
background: 'rgba(240, 185, 11, 0.1)',
|
||||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
border: '1px solid rgba(240, 185, 11, 0.2)',
|
||||||
}}
|
}}
|
||||||
whileHover={{ scale: 1.05 }}
|
|
||||||
>
|
>
|
||||||
<Target
|
<Terminal className="w-4 h-4" style={{ color: '#F0B90B' }} />
|
||||||
className="w-4 h-4"
|
<span className="text-xs font-medium" style={{ color: '#F0B90B' }}>
|
||||||
style={{ color: 'var(--brand-yellow)' }}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="text-sm font-semibold"
|
|
||||||
style={{ color: 'var(--brand-yellow)' }}
|
|
||||||
>
|
|
||||||
{t('aboutNofx', language)}
|
{t('aboutNofx', language)}
|
||||||
</span>
|
</span>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<h2
|
<h2 className="text-4xl lg:text-5xl font-bold mb-6" style={{ color: '#EAECEF' }}>
|
||||||
className="text-4xl font-bold"
|
|
||||||
style={{ color: 'var(--brand-light-gray)' }}
|
|
||||||
>
|
|
||||||
{t('whatIsNofx', language)}
|
{t('whatIsNofx', language)}
|
||||||
</h2>
|
</h2>
|
||||||
<p
|
|
||||||
className="text-lg leading-relaxed"
|
<p className="text-lg mb-8 leading-relaxed" style={{ color: '#848E9C' }}>
|
||||||
style={{ color: 'var(--text-secondary)' }}
|
{t('nofxNotAnotherBot', language)} {t('nofxDescription1', language)}
|
||||||
>
|
|
||||||
{t('nofxNotAnotherBot', language)}{' '}
|
|
||||||
{t('nofxDescription1', language)}{' '}
|
|
||||||
{t('nofxDescription2', language)}
|
|
||||||
</p>
|
</p>
|
||||||
<p
|
|
||||||
className="text-lg leading-relaxed"
|
{/* Feature Pills */}
|
||||||
style={{ color: 'var(--text-secondary)' }}
|
<div className="flex flex-wrap gap-3">
|
||||||
>
|
{features.map((feature, index) => (
|
||||||
{t('nofxDescription3', language)}{' '}
|
<motion.div
|
||||||
{t('nofxDescription4', language)}{' '}
|
key={feature.title}
|
||||||
{t('nofxDescription5', language)}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
</p>
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
<motion.div
|
viewport={{ once: true }}
|
||||||
className="flex items-center gap-3 pt-4"
|
transition={{ delay: index * 0.1 }}
|
||||||
whileHover={{ x: 5 }}
|
className="flex items-center gap-3 px-4 py-3 rounded-xl"
|
||||||
>
|
style={{
|
||||||
<div
|
background: 'rgba(255, 255, 255, 0.03)',
|
||||||
className="w-12 h-12 rounded-full flex items-center justify-center"
|
border: '1px solid rgba(255, 255, 255, 0.06)',
|
||||||
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)' }}
|
|
||||||
>
|
>
|
||||||
{t('youFullControl', language)}
|
<div
|
||||||
</div>
|
className="w-10 h-10 rounded-lg flex items-center justify-center"
|
||||||
<div
|
style={{ background: 'rgba(240, 185, 11, 0.1)' }}
|
||||||
className="text-sm"
|
>
|
||||||
style={{ color: 'var(--text-secondary)' }}
|
<feature.icon className="w-5 h-5" style={{ color: '#F0B90B' }} />
|
||||||
>
|
</div>
|
||||||
{t('fullControlDesc', language)}
|
<div>
|
||||||
</div>
|
<div className="text-sm font-semibold" style={{ color: '#EAECEF' }}>
|
||||||
</div>
|
{feature.title}
|
||||||
</motion.div>
|
</div>
|
||||||
|
<div className="text-xs" style={{ color: '#5E6673' }}>
|
||||||
|
{feature.desc}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</motion.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
|
<div
|
||||||
className="rounded-2xl p-8"
|
className="rounded-2xl overflow-hidden"
|
||||||
style={{
|
style={{
|
||||||
background: 'var(--brand-black)',
|
background: '#0D1117',
|
||||||
border: '1px solid var(--panel-border)',
|
border: '1px solid rgba(255, 255, 255, 0.1)',
|
||||||
|
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.5)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typewriter
|
{/* Terminal Header */}
|
||||||
lines={[
|
<div
|
||||||
'$ git clone https://github.com/tinkle-community/nofx.git',
|
className="flex items-center gap-2 px-4 py-3"
|
||||||
'$ cd nofx',
|
style={{ background: 'rgba(255, 255, 255, 0.03)', borderBottom: '1px solid rgba(255, 255, 255, 0.06)' }}
|
||||||
'$ chmod +x start.sh',
|
>
|
||||||
'$ ./start.sh start --build',
|
<div className="flex gap-2">
|
||||||
t('startupMessages1', language),
|
<div className="w-3 h-3 rounded-full" style={{ background: '#FF5F56' }} />
|
||||||
t('startupMessages2', language),
|
<div className="w-3 h-3 rounded-full" style={{ background: '#FFBD2E' }} />
|
||||||
t('startupMessages3', language),
|
<div className="w-3 h-3 rounded-full" style={{ background: '#27C93F' }} />
|
||||||
]}
|
</div>
|
||||||
typingSpeed={70}
|
<span className="text-xs ml-2" style={{ color: '#5E6673' }}>terminal</span>
|
||||||
lineDelay={900}
|
</div>
|
||||||
className="text-sm font-mono"
|
|
||||||
style={{
|
{/* Terminal Content */}
|
||||||
color: '#00FF88',
|
<div className="p-6 font-mono text-sm space-y-2">
|
||||||
textShadow: '0 0 8px rgba(0,255,136,0.4)',
|
<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>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AnimatedSection>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { motion } from 'framer-motion'
|
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
|
quote: string
|
||||||
authorName: string
|
authorName: string
|
||||||
handle: string
|
handle: string
|
||||||
@@ -10,49 +11,94 @@ interface CardProps {
|
|||||||
delay: number
|
delay: number
|
||||||
}
|
}
|
||||||
|
|
||||||
function TestimonialCard({ quote, authorName, delay }: CardProps) {
|
function TweetCard({ quote, authorName, handle, avatarUrl, tweetUrl, delay }: TweetProps) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.a
|
||||||
className="p-6 rounded-xl"
|
href={tweetUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="block p-5 rounded-2xl transition-all duration-300 group"
|
||||||
style={{
|
style={{
|
||||||
background: 'var(--brand-dark-gray)',
|
background: '#12161C',
|
||||||
border: '1px solid rgba(240, 185, 11, 0.1)',
|
border: '1px solid rgba(255, 255, 255, 0.06)',
|
||||||
}}
|
}}
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
transition={{ delay }}
|
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)' }}>
|
{/* Header */}
|
||||||
"{quote}"
|
<div className="flex items-start justify-between mb-3">
|
||||||
</p>
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex items-center gap-2">
|
<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
|
<div
|
||||||
className="w-8 h-8 rounded-full"
|
className="w-6 h-6 flex items-center justify-center opacity-50 group-hover:opacity-100 transition-opacity"
|
||||||
style={{ background: 'var(--binance-yellow)' }}
|
style={{ color: '#EAECEF' }}
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="text-sm font-semibold"
|
|
||||||
style={{ color: 'var(--text-secondary)' }}
|
|
||||||
>
|
>
|
||||||
{authorName}
|
<svg viewBox="0 0 24 24" className="w-4 h-4" fill="currentColor">
|
||||||
</span>
|
<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>
|
</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() {
|
interface CommunitySectionProps {
|
||||||
const staggerContainer = {
|
language?: Language
|
||||||
animate: { transition: { staggerChildren: 0.1 } },
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 推特内容整合(保持原三列布局,超出自动换行)
|
export default function CommunitySection({ language }: CommunitySectionProps) {
|
||||||
const items: CardProps[] = [
|
const tweets: TweetProps[] = [
|
||||||
{
|
{
|
||||||
quote:
|
quote:
|
||||||
'前不久非常火的 AI 量化交易系统 NOF1,在 GitHub 上有人将其复刻并开源,这就是 NOFX 项目。基于 DeepSeek、Qwen 等大语言模型,打造的通用架构 AI 交易操作系统,完成了从决策、到交易、再到复盘的闭环。GitHub: https://github.com/NoFxAiOS/nofx',
|
'前不久非常火的 AI 量化交易系统 NOF1,在 GitHub 上有人将其复刻并开源,这就是 NOFX 项目。基于 DeepSeek、Qwen 等大语言模型,打造的通用架构 AI 交易操作系统,完成了从决策、到交易、再到复盘的闭环。',
|
||||||
authorName: 'Michael Williams',
|
authorName: 'Michael Williams',
|
||||||
handle: '@MichaelWil93725',
|
handle: '@MichaelWil93725',
|
||||||
avatarUrl:
|
avatarUrl:
|
||||||
@@ -84,20 +130,61 @@ export default function CommunitySection() {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedSection>
|
<section className="py-24 relative" style={{ background: '#0B0E11' }}>
|
||||||
<div className="max-w-7xl mx-auto">
|
{/* 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
|
<motion.div
|
||||||
className="grid md:grid-cols-3 gap-6"
|
className="text-center mb-12"
|
||||||
variants={staggerContainer}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
initial="initial"
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
whileInView="animate"
|
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
>
|
>
|
||||||
{items.map((item, idx) => (
|
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#EAECEF' }}>
|
||||||
<TestimonialCard key={idx} {...item} />
|
{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>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</AnimatedSection>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import AnimatedSection from './AnimatedSection'
|
import { Brain, Swords, BarChart3, Shield, Blocks, LineChart } from 'lucide-react'
|
||||||
import { CryptoFeatureCard } from '../CryptoFeatureCard'
|
|
||||||
import { Code, Cpu, Lock, Rocket } from 'lucide-react'
|
|
||||||
import { t, Language } from '../../i18n/translations'
|
import { t, Language } from '../../i18n/translations'
|
||||||
|
|
||||||
interface FeaturesSectionProps {
|
interface FeaturesSectionProps {
|
||||||
@@ -9,84 +7,199 @@ interface FeaturesSectionProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function FeaturesSection({ language }: 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 (
|
return (
|
||||||
<AnimatedSection id="features">
|
<section className="py-24 relative" style={{ background: '#0B0E11' }}>
|
||||||
<div className="max-w-7xl mx-auto">
|
{/* 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
|
<motion.div
|
||||||
className="text-center mb-16"
|
className="text-center mb-16"
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
>
|
>
|
||||||
<motion.div
|
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#EAECEF' }}>
|
||||||
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)' }}
|
|
||||||
>
|
|
||||||
{t('whyChooseNofx', language)}
|
{t('whyChooseNofx', language)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg" style={{ color: 'var(--text-secondary)' }}>
|
<p className="text-lg max-w-2xl mx-auto" style={{ color: '#848E9C' }}>
|
||||||
{t('openCommunityDriven', language)}
|
{language === 'zh'
|
||||||
|
? '不只是交易机器人,而是完整的 AI 交易操作系统'
|
||||||
|
: 'Not just a trading bot, but a complete AI trading operating system'}
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
{/* Features Grid */}
|
||||||
<CryptoFeatureCard
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||||
icon={<Code className="w-8 h-8" />}
|
{features.map((feature, index) => (
|
||||||
title={t('openSourceSelfHosted', language)}
|
<motion.div
|
||||||
description={t('openSourceDesc', language)}
|
key={feature.title}
|
||||||
features={[
|
initial={{ opacity: 0, y: 20 }}
|
||||||
t('openSourceFeatures1', language),
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
t('openSourceFeatures2', language),
|
viewport={{ once: true }}
|
||||||
t('openSourceFeatures3', language),
|
transition={{ delay: index * 0.1 }}
|
||||||
t('openSourceFeatures4', language),
|
className={`
|
||||||
]}
|
relative group rounded-2xl p-6 transition-all duration-300
|
||||||
delay={0}
|
${feature.highlight ? 'md:col-span-1 lg:col-span-1' : ''}
|
||||||
/>
|
`}
|
||||||
<CryptoFeatureCard
|
style={{
|
||||||
icon={<Cpu className="w-8 h-8" />}
|
background: feature.highlight
|
||||||
title={t('multiAgentCompetition', language)}
|
? 'linear-gradient(135deg, rgba(240, 185, 11, 0.08) 0%, rgba(240, 185, 11, 0.02) 100%)'
|
||||||
description={t('multiAgentDesc', language)}
|
: '#12161C',
|
||||||
features={[
|
border: feature.highlight
|
||||||
t('multiAgentFeatures1', language),
|
? '1px solid rgba(240, 185, 11, 0.2)'
|
||||||
t('multiAgentFeatures2', language),
|
: '1px solid rgba(255, 255, 255, 0.06)',
|
||||||
t('multiAgentFeatures3', language),
|
}}
|
||||||
t('multiAgentFeatures4', language),
|
>
|
||||||
]}
|
{/* Badge */}
|
||||||
delay={0.1}
|
{feature.badge && (
|
||||||
/>
|
<div
|
||||||
<CryptoFeatureCard
|
className="absolute top-4 right-4 px-2 py-1 rounded text-xs font-medium"
|
||||||
icon={<Lock className="w-8 h-8" />}
|
style={{
|
||||||
title={t('secureReliableTrading', language)}
|
background: 'rgba(240, 185, 11, 0.15)',
|
||||||
description={t('secureDesc', language)}
|
color: '#F0B90B',
|
||||||
features={[
|
}}
|
||||||
t('secureFeatures1', language),
|
>
|
||||||
t('secureFeatures2', language),
|
{feature.badge}
|
||||||
t('secureFeatures3', language),
|
</div>
|
||||||
t('secureFeatures4', language),
|
)}
|
||||||
]}
|
|
||||||
delay={0.2}
|
{/* 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>
|
</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>
|
</div>
|
||||||
</AnimatedSection>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,180 +1,169 @@
|
|||||||
|
import { Github, Send, ExternalLink } from 'lucide-react'
|
||||||
import { t, Language } from '../../i18n/translations'
|
import { t, Language } from '../../i18n/translations'
|
||||||
|
import { OFFICIAL_LINKS } from '../../constants/branding'
|
||||||
|
|
||||||
interface FooterSectionProps {
|
interface FooterSectionProps {
|
||||||
language: Language
|
language: Language
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FooterSection({ language }: FooterSectionProps) {
|
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 (
|
return (
|
||||||
<footer
|
<footer style={{ background: '#0B0E11', borderTop: '1px solid rgba(255, 255, 255, 0.06)' }}>
|
||||||
style={{
|
<div className="max-w-6xl mx-auto px-4 py-12">
|
||||||
borderTop: '1px solid var(--panel-border)',
|
{/* Top Section */}
|
||||||
background: 'var(--brand-dark-gray)',
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-10 mb-12">
|
||||||
}}
|
{/* Brand */}
|
||||||
>
|
<div className="md:col-span-1">
|
||||||
<div className="max-w-[1200px] mx-auto px-6 py-10">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
{/* Brand */}
|
<img src="/icons/nofx.svg" alt="NOFX Logo" className="w-8 h-8" />
|
||||||
<div className="flex items-center gap-3 mb-8">
|
<span className="text-xl font-bold" style={{ color: '#EAECEF' }}>
|
||||||
<img src="/icons/nofx.svg" alt="NOFX Logo" className="w-8 h-8" />
|
NOFX
|
||||||
<div>
|
</span>
|
||||||
<div className="text-lg font-bold" style={{ color: '#EAECEF' }}>
|
|
||||||
NOFX
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs" style={{ color: '#848E9C' }}>
|
<p className="text-sm mb-6" style={{ color: '#5E6673' }}>
|
||||||
{t('futureStandardAI', language)}
|
{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>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Multi-link columns */}
|
{/* Links */}
|
||||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-3 gap-8">
|
|
||||||
<div>
|
<div>
|
||||||
<h3
|
<h4 className="text-sm font-semibold mb-4" style={{ color: '#EAECEF' }}>
|
||||||
className="text-sm font-semibold mb-3"
|
|
||||||
style={{ color: '#EAECEF' }}
|
|
||||||
>
|
|
||||||
{t('links', language)}
|
{t('links', language)}
|
||||||
</h3>
|
</h4>
|
||||||
<ul className="space-y-2 text-sm" style={{ color: '#848E9C' }}>
|
<ul className="space-y-3">
|
||||||
<li>
|
{links.social.map((link) => (
|
||||||
<a
|
<li key={link.name}>
|
||||||
className="hover:text-[#F0B90B]"
|
<a
|
||||||
href="https://github.com/tinkle-community/nofx"
|
href={link.href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
className="text-sm transition-colors hover:text-[#F0B90B]"
|
||||||
GitHub
|
style={{ color: '#5E6673' }}
|
||||||
</a>
|
>
|
||||||
</li>
|
{link.name}
|
||||||
<li>
|
</a>
|
||||||
<a
|
</li>
|
||||||
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>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Resources */}
|
||||||
<div>
|
<div>
|
||||||
<h3
|
<h4 className="text-sm font-semibold mb-4" style={{ color: '#EAECEF' }}>
|
||||||
className="text-sm font-semibold mb-3"
|
|
||||||
style={{ color: '#EAECEF' }}
|
|
||||||
>
|
|
||||||
{t('resources', language)}
|
{t('resources', language)}
|
||||||
</h3>
|
</h4>
|
||||||
<ul className="space-y-2 text-sm" style={{ color: '#848E9C' }}>
|
<ul className="space-y-3">
|
||||||
<li>
|
{links.resources.map((link) => (
|
||||||
<a
|
<li key={link.name}>
|
||||||
className="hover:text-[#F0B90B]"
|
<a
|
||||||
href="https://github.com/tinkle-community/nofx/blob/main/README.md"
|
href={link.href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
className="text-sm transition-colors hover:text-[#F0B90B] inline-flex items-center gap-1"
|
||||||
{t('documentation', language)}
|
style={{ color: '#5E6673' }}
|
||||||
</a>
|
>
|
||||||
</li>
|
{link.name}
|
||||||
<li>
|
<ExternalLink className="w-3 h-3 opacity-50" />
|
||||||
<a
|
</a>
|
||||||
className="hover:text-[#F0B90B]"
|
</li>
|
||||||
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>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Supporters */}
|
||||||
<div>
|
<div>
|
||||||
<h3
|
<h4 className="text-sm font-semibold mb-4" style={{ color: '#EAECEF' }}>
|
||||||
className="text-sm font-semibold mb-3"
|
|
||||||
style={{ color: '#EAECEF' }}
|
|
||||||
>
|
|
||||||
{t('supporters', language)}
|
{t('supporters', language)}
|
||||||
</h3>
|
</h4>
|
||||||
<ul className="space-y-2 text-sm" style={{ color: '#848E9C' }}>
|
<ul className="space-y-3">
|
||||||
<li>
|
{links.supporters.map((link) => (
|
||||||
<a
|
<li key={link.name}>
|
||||||
className="hover:text-[#F0B90B]"
|
<a
|
||||||
href="https://www.asterdex.com/en/referral/fdfc0e"
|
href={link.href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
className="text-sm transition-colors hover:text-[#F0B90B] inline-flex items-center gap-2"
|
||||||
Aster DEX
|
style={{ color: '#5E6673' }}
|
||||||
</a>
|
>
|
||||||
</li>
|
{link.name}
|
||||||
<li>
|
{link.badge && (
|
||||||
<a
|
<span
|
||||||
className="hover:text-[#F0B90B]"
|
className="text-xs px-1.5 py-0.5 rounded"
|
||||||
href="https://www.maxweb.red/join?ref=NOFXAI"
|
style={{
|
||||||
target="_blank"
|
background: 'rgba(240, 185, 11, 0.1)',
|
||||||
rel="noopener noreferrer"
|
color: '#F0B90B',
|
||||||
>
|
}}
|
||||||
Binance
|
>
|
||||||
</a>
|
{link.badge}
|
||||||
</li>
|
</span>
|
||||||
<li>
|
)}
|
||||||
<a
|
</a>
|
||||||
className="hover:text-[#F0B90B]"
|
</li>
|
||||||
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>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom note (kept subtle) */}
|
{/* Bottom Section */}
|
||||||
<div
|
<div
|
||||||
className="pt-6 mt-8 text-center text-xs"
|
className="pt-6 text-center text-xs"
|
||||||
style={{
|
style={{ color: '#5E6673', borderTop: '1px solid rgba(255, 255, 255, 0.06)' }}
|
||||||
color: 'var(--text-tertiary)',
|
|
||||||
borderTop: '1px solid var(--panel-border)',
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<p>{t('footerTitle', language)}</p>
|
<p className="mb-2">{t('footerTitle', language)}</p>
|
||||||
<p className="mt-1">{t('footerWarning', language)}</p>
|
<p style={{ color: '#3C4249' }}>{t('footerWarning', language)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -1,214 +1,252 @@
|
|||||||
import { motion, useScroll, useTransform, useAnimation } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { Sparkles } from 'lucide-react'
|
import { ArrowRight, Play, Github, Zap } from 'lucide-react'
|
||||||
import { t, Language } from '../../i18n/translations'
|
import { t, Language } from '../../i18n/translations'
|
||||||
import { useGitHubStats } from '../../hooks/useGitHubStats'
|
import { useGitHubStats } from '../../hooks/useGitHubStats'
|
||||||
import { useCounterAnimation } from '../../hooks/useCounterAnimation'
|
import { useCounterAnimation } from '../../hooks/useCounterAnimation'
|
||||||
|
import { OFFICIAL_LINKS } from '../../constants/branding'
|
||||||
|
|
||||||
interface HeroSectionProps {
|
interface HeroSectionProps {
|
||||||
language: Language
|
language: Language
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HeroSection({ language }: HeroSectionProps) {
|
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')
|
const { stars, daysOld, isLoading } = useGitHubStats('NoFxAiOS', 'nofx')
|
||||||
|
|
||||||
// 动画数字 - 仅对 stars 添加动画
|
|
||||||
const animatedStars = useCounterAnimation({
|
const animatedStars = useCounterAnimation({
|
||||||
start: 0,
|
start: 0,
|
||||||
end: stars,
|
end: stars,
|
||||||
duration: 2000,
|
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 (
|
return (
|
||||||
<section className="relative pt-32 pb-20 px-4">
|
<section className="relative min-h-screen flex items-center justify-center overflow-hidden pt-16">
|
||||||
<div className="max-w-7xl mx-auto">
|
{/* Animated Background */}
|
||||||
<div className="grid lg:grid-cols-2 gap-12 items-center">
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
{/* Left Content */}
|
{/* Grid Pattern */}
|
||||||
<motion.div
|
<div
|
||||||
className="space-y-6 relative z-10"
|
className="absolute inset-0 opacity-[0.03]"
|
||||||
style={{ opacity, scale }}
|
style={{
|
||||||
initial="initial"
|
backgroundImage: `linear-gradient(#F0B90B 1px, transparent 1px), linear-gradient(90deg, #F0B90B 1px, transparent 1px)`,
|
||||||
animate="animate"
|
backgroundSize: '60px 60px',
|
||||||
variants={staggerContainer}
|
}}
|
||||||
>
|
/>
|
||||||
<motion.div variants={fadeInUp}>
|
{/* Radial Gradient */}
|
||||||
<motion.div
|
<div
|
||||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full mb-6"
|
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] rounded-full"
|
||||||
style={{
|
style={{
|
||||||
background: 'rgba(240, 185, 11, 0.1)',
|
background: 'radial-gradient(circle, rgba(240, 185, 11, 0.08) 0%, transparent 70%)',
|
||||||
border: '1px solid rgba(240, 185, 11, 0.2)',
|
}}
|
||||||
}}
|
/>
|
||||||
whileHover={{
|
{/* Floating Orbs */}
|
||||||
scale: 1.05,
|
<motion.div
|
||||||
boxShadow: '0 0 20px rgba(240, 185, 11, 0.2)',
|
className="absolute top-20 right-20 w-32 h-32 rounded-full blur-3xl"
|
||||||
}}
|
style={{ background: 'rgba(240, 185, 11, 0.15)' }}
|
||||||
>
|
animate={{
|
||||||
<Sparkles
|
y: [0, 30, 0],
|
||||||
className="w-4 h-4"
|
scale: [1, 1.1, 1],
|
||||||
style={{ color: 'var(--brand-yellow)' }}
|
}}
|
||||||
/>
|
transition={{ duration: 8, repeat: Infinity, ease: 'easeInOut' }}
|
||||||
<span
|
/>
|
||||||
className="text-sm font-semibold"
|
<motion.div
|
||||||
style={{ color: 'var(--brand-yellow)' }}
|
className="absolute bottom-40 left-20 w-48 h-48 rounded-full blur-3xl"
|
||||||
>
|
style={{ background: 'rgba(240, 185, 11, 0.1)' }}
|
||||||
{isLoading ? (
|
animate={{
|
||||||
t('githubStarsInDays', language)
|
y: [0, -40, 0],
|
||||||
) : language === 'zh' ? (
|
scale: [1, 1.2, 1],
|
||||||
<>
|
}}
|
||||||
{daysOld} 天内{' '}
|
transition={{ duration: 10, repeat: Infinity, ease: 'easeInOut' }}
|
||||||
<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>
|
|
||||||
</div>
|
</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>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +1,167 @@
|
|||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import AnimatedSection from './AnimatedSection'
|
import { Download, Rocket, TrendingUp, AlertTriangle } from 'lucide-react'
|
||||||
import { t, Language } from '../../i18n/translations'
|
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 {
|
interface HowItWorksSectionProps {
|
||||||
language: Language
|
language: Language
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function HowItWorksSection({
|
export default function HowItWorksSection({ language }: HowItWorksSectionProps) {
|
||||||
language,
|
const steps = [
|
||||||
}: HowItWorksSectionProps) {
|
{
|
||||||
|
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 (
|
return (
|
||||||
<AnimatedSection id="how-it-works" backgroundColor="var(--brand-dark-gray)">
|
<section className="py-24 relative overflow-hidden" style={{ background: '#0D1117' }}>
|
||||||
<div className="max-w-7xl mx-auto">
|
{/* 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
|
<motion.div
|
||||||
className="text-center mb-16"
|
className="text-center mb-16"
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
>
|
>
|
||||||
<h2
|
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#EAECEF' }}>
|
||||||
className="text-4xl font-bold mb-4"
|
|
||||||
style={{ color: 'var(--brand-light-gray)' }}
|
|
||||||
>
|
|
||||||
{t('howToStart', language)}
|
{t('howToStart', language)}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg" style={{ color: 'var(--text-secondary)' }}>
|
<p className="text-lg" style={{ color: '#848E9C' }}>
|
||||||
{t('fourSimpleSteps', language)}
|
{t('fourSimpleSteps', language)}
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<div className="space-y-8">
|
{/* Steps Timeline */}
|
||||||
{[
|
<div className="relative">
|
||||||
{
|
{/* Connecting Line */}
|
||||||
number: 1,
|
<div
|
||||||
title: t('step1Title', language),
|
className="absolute left-[39px] top-0 bottom-0 w-px hidden lg:block"
|
||||||
description: t('step1Desc', language),
|
style={{ background: 'linear-gradient(to bottom, transparent, rgba(240, 185, 11, 0.3), transparent)' }}
|
||||||
},
|
/>
|
||||||
{
|
|
||||||
number: 2,
|
<div className="space-y-6">
|
||||||
title: t('step2Title', language),
|
{steps.map((step, index) => (
|
||||||
description: t('step2Desc', language),
|
<motion.div
|
||||||
},
|
key={step.number}
|
||||||
{
|
initial={{ opacity: 0, x: -30 }}
|
||||||
number: 3,
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
title: t('step3Title', language),
|
viewport={{ once: true }}
|
||||||
description: t('step3Desc', language),
|
transition={{ delay: index * 0.15 }}
|
||||||
},
|
className="relative"
|
||||||
{
|
>
|
||||||
number: 4,
|
<div
|
||||||
title: t('step4Title', language),
|
className="flex flex-col lg:flex-row items-start gap-6 p-6 rounded-2xl transition-all duration-300 hover:translate-x-2"
|
||||||
description: t('step4Desc', language),
|
style={{
|
||||||
},
|
background: 'rgba(255, 255, 255, 0.02)',
|
||||||
].map((step, index) => (
|
border: '1px solid rgba(255, 255, 255, 0.05)',
|
||||||
<StepCard key={step.number} {...step} delay={index * 0.1} />
|
}}
|
||||||
))}
|
>
|
||||||
|
{/* 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>
|
</div>
|
||||||
|
|
||||||
|
{/* Risk Warning */}
|
||||||
<motion.div
|
<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={{
|
style={{
|
||||||
background: 'rgba(246, 70, 93, 0.1)',
|
background: 'rgba(240, 185, 11, 0.05)',
|
||||||
border: '1px solid rgba(246, 70, 93, 0.3)',
|
border: '1px solid rgba(240, 185, 11, 0.15)',
|
||||||
}}
|
}}
|
||||||
initial={{ opacity: 0, scale: 0.9 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, scale: 1 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
whileHover={{ scale: 1.02 }}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0"
|
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
|
||||||
style={{ background: 'rgba(246, 70, 93, 0.2)', color: '#F6465D' }}
|
style={{ background: 'rgba(240, 185, 11, 0.1)' }}
|
||||||
>
|
>
|
||||||
<svg
|
<AlertTriangle className="w-6 h-6" style={{ color: '#F0B90B' }} />
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-semibold mb-2" style={{ color: '#F6465D' }}>
|
<div className="font-semibold mb-2" style={{ color: '#F0B90B' }}>
|
||||||
{t('importantRiskWarning', language)}
|
{t('importantRiskWarning', language)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
<p className="text-sm leading-relaxed" style={{ color: '#5E6673' }}>
|
||||||
{t('riskWarningText', language)}
|
{t('riskWarningText', language)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</AnimatedSection>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
WebCryptoEnvironmentCheck,
|
WebCryptoEnvironmentCheck,
|
||||||
type WebCryptoCheckStatus,
|
type WebCryptoCheckStatus,
|
||||||
} from '../WebCryptoEnvironmentCheck'
|
} from '../WebCryptoEnvironmentCheck'
|
||||||
import { BookOpen, Trash2, HelpCircle } from 'lucide-react'
|
import { BookOpen, Trash2, HelpCircle, ExternalLink, UserPlus } from 'lucide-react'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { Tooltip } from './Tooltip'
|
import { Tooltip } from './Tooltip'
|
||||||
import { getShortName } from './utils'
|
import { getShortName } from './utils'
|
||||||
@@ -92,6 +92,16 @@ export function ExchangeConfigModal({
|
|||||||
(e) => e.id === selectedExchangeId
|
(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(() => {
|
useEffect(() => {
|
||||||
if (editingExchangeId && selectedExchange) {
|
if (editingExchangeId && selectedExchange) {
|
||||||
@@ -411,6 +421,34 @@ export function ExchangeConfigModal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { motion } from 'framer-motion'
|
import { motion } from 'framer-motion'
|
||||||
import { ArrowRight } from 'lucide-react'
|
import { ArrowRight, Github } from 'lucide-react'
|
||||||
import HeaderBar from '../components/HeaderBar'
|
import HeaderBar from '../components/HeaderBar'
|
||||||
import HeroSection from '../components/landing/HeroSection'
|
import HeroSection from '../components/landing/HeroSection'
|
||||||
import AboutSection from '../components/landing/AboutSection'
|
import AboutSection from '../components/landing/AboutSection'
|
||||||
import FeaturesSection from '../components/landing/FeaturesSection'
|
import FeaturesSection from '../components/landing/FeaturesSection'
|
||||||
import HowItWorksSection from '../components/landing/HowItWorksSection'
|
import HowItWorksSection from '../components/landing/HowItWorksSection'
|
||||||
import CommunitySection from '../components/landing/CommunitySection'
|
import CommunitySection from '../components/landing/CommunitySection'
|
||||||
import AnimatedSection from '../components/landing/AnimatedSection'
|
|
||||||
import LoginModal from '../components/landing/LoginModal'
|
import LoginModal from '../components/landing/LoginModal'
|
||||||
import FooterSection from '../components/landing/FooterSection'
|
import FooterSection from '../components/landing/FooterSection'
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
import { useLanguage } from '../contexts/LanguageContext'
|
import { useLanguage } from '../contexts/LanguageContext'
|
||||||
import { t } from '../i18n/translations'
|
import { t } from '../i18n/translations'
|
||||||
|
import { OFFICIAL_LINKS } from '../constants/branding'
|
||||||
|
|
||||||
export function LandingPage() {
|
export function LandingPage() {
|
||||||
const [showLoginModal, setShowLoginModal] = useState(false)
|
const [showLoginModal, setShowLoginModal] = useState(false)
|
||||||
@@ -20,7 +20,6 @@ export function LandingPage() {
|
|||||||
const { language, setLanguage } = useLanguage()
|
const { language, setLanguage } = useLanguage()
|
||||||
const isLoggedIn = !!user
|
const isLoggedIn = !!user
|
||||||
|
|
||||||
console.log('LandingPage - user:', user, 'isLoggedIn:', isLoggedIn)
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeaderBar
|
<HeaderBar
|
||||||
@@ -32,7 +31,6 @@ export function LandingPage() {
|
|||||||
user={user}
|
user={user}
|
||||||
onLogout={logout}
|
onLogout={logout}
|
||||||
onPageChange={(page) => {
|
onPageChange={(page) => {
|
||||||
console.log('LandingPage onPageChange called with:', page)
|
|
||||||
if (page === 'competition') {
|
if (page === 'competition') {
|
||||||
window.location.href = '/competition'
|
window.location.href = '/competition'
|
||||||
} else if (page === 'traders') {
|
} else if (page === 'traders') {
|
||||||
@@ -43,24 +41,30 @@ export function LandingPage() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="min-h-screen px-4 sm:px-6 lg:px-8"
|
className="min-h-screen"
|
||||||
style={{
|
style={{
|
||||||
background: 'var(--brand-black)',
|
background: '#0B0E11',
|
||||||
color: 'var(--brand-light-gray)',
|
color: '#EAECEF',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HeroSection language={language} />
|
<HeroSection language={language} />
|
||||||
<AboutSection language={language} />
|
<AboutSection language={language} />
|
||||||
<FeaturesSection language={language} />
|
<FeaturesSection language={language} />
|
||||||
<HowItWorksSection language={language} />
|
<HowItWorksSection language={language} />
|
||||||
<CommunitySection />
|
<CommunitySection language={language} />
|
||||||
|
|
||||||
{/* CTA */}
|
{/* Final CTA Section */}
|
||||||
<AnimatedSection backgroundColor="var(--panel-bg)">
|
<section className="py-24 relative overflow-hidden" style={{ background: '#0D1117' }}>
|
||||||
<div className="max-w-4xl mx-auto text-center">
|
{/* 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
|
<motion.h2
|
||||||
className="text-5xl font-bold mb-6"
|
className="text-4xl lg:text-5xl font-bold mb-6"
|
||||||
style={{ color: 'var(--brand-light-gray)' }}
|
style={{ color: '#EAECEF' }}
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
@@ -68,8 +72,8 @@ export function LandingPage() {
|
|||||||
{t('readyToDefine', language)}
|
{t('readyToDefine', language)}
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
<motion.p
|
<motion.p
|
||||||
className="text-xl mb-12"
|
className="text-lg mb-10 max-w-2xl mx-auto"
|
||||||
style={{ color: 'var(--text-secondary)' }}
|
style={{ color: '#848E9C' }}
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true }}
|
viewport={{ once: true }}
|
||||||
@@ -77,46 +81,55 @@ export function LandingPage() {
|
|||||||
>
|
>
|
||||||
{t('startWithCrypto', language)}
|
{t('startWithCrypto', language)}
|
||||||
</motion.p>
|
</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
|
<motion.button
|
||||||
onClick={() => setShowLoginModal(true)}
|
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={{
|
style={{
|
||||||
background: 'var(--brand-yellow)',
|
background: 'linear-gradient(135deg, #F0B90B 0%, #FCD535 100%)',
|
||||||
color: 'var(--brand-black)',
|
color: '#0B0E11',
|
||||||
}}
|
boxShadow: '0 4px 24px rgba(240, 185, 11, 0.3)',
|
||||||
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)',
|
|
||||||
}}
|
}}
|
||||||
whileHover={{
|
whileHover={{
|
||||||
scale: 1.05,
|
scale: 1.02,
|
||||||
backgroundColor: 'rgba(240, 185, 11, 0.1)',
|
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)}
|
{t('viewSourceCode', language)}
|
||||||
</motion.a>
|
</motion.a>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</AnimatedSection>
|
</section>
|
||||||
|
|
||||||
{showLoginModal && (
|
{showLoginModal && (
|
||||||
<LoginModal
|
<LoginModal
|
||||||
|
|||||||
Reference in New Issue
Block a user