mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-09 22:10:57 +08:00
feat: beginner-first funnel — plain-language guidance from landing to first trade
A newcomer who can't get from install to a running trade in minutes churns.
This pass removes every jargon wall on that path:
- Landing hero: primary CTA is now 'START THE AUTOPILOT' (routes into the
guided flow; logged-out users get the guest walkthrough) with a
plain-language promise line — self-hosted, ~$13 to start, no API keys.
'CREATE STOCK TRADER' demoted to a 'SEE IT WORK' scroll button.
- Deploy section: 'high-frequency trading node / sandboxed execution env'
copy replaced with plain words plus the first-five-minutes checklist
(register → fund $1 + $12 → press start).
- Config page: the guided launch panel now renders FIRST for anyone without
a running autopilot; the config grid becomes detail below. Steps renamed
to 'Step 1 · Fund the AI wallet ($1+)' etc., each explained in one plain
sentence with explicit dollar amounts and safety notes ('it can never
withdraw your money').
- Every AI-wallet action (deposit, create, preflight fix-links) now lands on
the /welcome page with the QR code and auto-refreshing balance instead of
the key-config modal.
- README: first-run three-step guide under the install command (banner asset
included); six dead landing components removed.
Tests: 128 web tests green; funnel click-verified logged-out and logged-in.
This commit is contained in:
@@ -1,157 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Terminal, Shield, Cpu, BarChart3 } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
|
||||
interface AboutSectionProps {
|
||||
language: Language
|
||||
}
|
||||
|
||||
export default function AboutSection({ language }: AboutSectionProps) {
|
||||
const features = [
|
||||
{
|
||||
icon: Shield,
|
||||
title: language === 'zh' ? 'Full Control' : 'Full Control',
|
||||
desc: language === 'zh' ? 'Self-hosted, data secure' : 'Self-hosted, data secure',
|
||||
},
|
||||
{
|
||||
icon: Cpu,
|
||||
title: language === 'zh' ? 'Multi-AI Support' : 'Multi-AI Support',
|
||||
desc: language === 'zh' ? 'DeepSeek, GPT, Claude...' : 'DeepSeek, GPT, Claude...',
|
||||
},
|
||||
{
|
||||
icon: BarChart3,
|
||||
title: language === 'zh' ? 'Real-time Monitor' : 'Real-time Monitor',
|
||||
desc: language === 'zh' ? 'Visual trading dashboard' : 'Visual trading dashboard',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="py-24 relative overflow-hidden" style={{ background: '#F1ECE2' }}>
|
||||
{/* Background Decoration */}
|
||||
<div
|
||||
className="absolute top-0 right-0 w-96 h-96 rounded-full blur-3xl opacity-30"
|
||||
style={{ background: 'radial-gradient(circle, rgba(224, 72, 59, 0.08) 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
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
<motion.div
|
||||
className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full mb-6"
|
||||
style={{
|
||||
background: 'rgba(224, 72, 59, 0.1)',
|
||||
border: '1px solid rgba(224, 72, 59, 0.2)',
|
||||
}}
|
||||
>
|
||||
<Terminal className="w-4 h-4" style={{ color: '#E0483B' }} />
|
||||
<span className="text-xs font-medium" style={{ color: '#E0483B' }}>
|
||||
{t('aboutNofx', language)}
|
||||
</span>
|
||||
</motion.div>
|
||||
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-6" style={{ color: '#1A1813' }}>
|
||||
{t('whatIsNofx', language)}
|
||||
</h2>
|
||||
|
||||
<p className="text-lg mb-8 leading-relaxed" style={{ color: '#8A8478' }}>
|
||||
{t('nofxNotAnotherBot', language)} {t('nofxDescription1', language)}
|
||||
</p>
|
||||
|
||||
{/* Feature Pills */}
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="flex items-center gap-3 px-4 py-3 rounded-xl"
|
||||
style={{
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-10 h-10 rounded-lg flex items-center justify-center"
|
||||
style={{ background: 'rgba(224, 72, 59, 0.1)' }}
|
||||
>
|
||||
<feature.icon className="w-5 h-5" style={{ color: '#E0483B' }} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-semibold" style={{ color: '#1A1813' }}>
|
||||
{feature.title}
|
||||
</div>
|
||||
<div className="text-xs" style={{ color: '#8A8478' }}>
|
||||
{feature.desc}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Right - Terminal */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
>
|
||||
<div
|
||||
className="rounded-2xl overflow-hidden"
|
||||
style={{
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
boxShadow: '0 6px 24px rgba(26, 24, 19, 0.12)',
|
||||
}}
|
||||
>
|
||||
{/* Terminal Header */}
|
||||
<div
|
||||
className="flex items-center gap-2 px-4 py-3"
|
||||
style={{ background: '#E8E2D5', borderBottom: '1px solid rgba(26, 24, 19, 0.14)' }}
|
||||
>
|
||||
<div className="flex gap-2">
|
||||
<div className="w-3 h-3 rounded-full" style={{ background: '#D6433A' }} />
|
||||
<div className="w-3 h-3 rounded-full" style={{ background: '#E0483B' }} />
|
||||
<div className="w-3 h-3 rounded-full" style={{ background: '#2E8B57' }} />
|
||||
</div>
|
||||
<span className="text-xs ml-2" style={{ color: '#8A8478' }}>terminal</span>
|
||||
</div>
|
||||
|
||||
{/* Terminal Content */}
|
||||
<div className="p-6 font-mono text-sm space-y-2">
|
||||
<div style={{ color: '#8A8478' }}>$ git clone https://github.com/NoFxAiOS/nofx.git</div>
|
||||
<div style={{ color: '#8A8478' }}>$ cd nofx && chmod +x start.sh</div>
|
||||
<div style={{ color: '#8A8478' }}>$ ./start.sh start --build</div>
|
||||
<div className="pt-2" style={{ color: '#E0483B' }}>
|
||||
✓ {t('startupMessages1', language)}
|
||||
</div>
|
||||
<div style={{ color: '#2E8B57' }}>
|
||||
✓ {t('startupMessages2', language)}
|
||||
</div>
|
||||
<div style={{ color: '#2E8B57' }}>
|
||||
✓ {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: '#E0483B' }}>▸</span>
|
||||
<span style={{ color: '#1A1813' }}>_</span>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { useRef } from 'react'
|
||||
import { motion, useInView } from 'framer-motion'
|
||||
|
||||
export default function AnimatedSection({
|
||||
children,
|
||||
id,
|
||||
backgroundColor = '#F1ECE2',
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
id?: string
|
||||
backgroundColor?: string
|
||||
}) {
|
||||
const ref = useRef(null)
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' })
|
||||
|
||||
return (
|
||||
<motion.section
|
||||
id={id}
|
||||
ref={ref}
|
||||
className="py-20 px-4"
|
||||
style={{ background: backgroundColor }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
>
|
||||
{children}
|
||||
</motion.section>
|
||||
)
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { MessageCircle, Heart, Repeat2, ExternalLink } from 'lucide-react'
|
||||
import { Language } from '../../i18n/translations'
|
||||
|
||||
interface TweetProps {
|
||||
quote: string
|
||||
authorName: string
|
||||
handle: string
|
||||
avatarUrl: string
|
||||
tweetUrl: string
|
||||
delay: number
|
||||
}
|
||||
|
||||
function TweetCard({ quote, authorName, handle, avatarUrl, tweetUrl, delay }: TweetProps) {
|
||||
return (
|
||||
<motion.a
|
||||
href={tweetUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block p-5 rounded-2xl transition-all duration-300 group"
|
||||
style={{
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay }}
|
||||
whileHover={{
|
||||
y: -4,
|
||||
borderColor: 'rgba(224, 72, 59, 0.3)',
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<img
|
||||
src={avatarUrl}
|
||||
alt={authorName}
|
||||
className="w-10 h-10 rounded-full object-cover"
|
||||
style={{ border: '2px solid rgba(26, 24, 19, 0.14)' }}
|
||||
/>
|
||||
<div>
|
||||
<div className="font-semibold text-sm" style={{ color: '#1A1813' }}>
|
||||
{authorName}
|
||||
</div>
|
||||
<div className="text-xs" style={{ color: '#8A8478' }}>
|
||||
{handle}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* X Logo */}
|
||||
<div
|
||||
className="w-6 h-6 flex items-center justify-center opacity-50 group-hover:opacity-100 transition-opacity"
|
||||
style={{ color: '#1A1813' }}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" className="w-4 h-4" fill="currentColor">
|
||||
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<p
|
||||
className="text-sm leading-relaxed mb-4 line-clamp-4"
|
||||
style={{ color: '#1A1813' }}
|
||||
>
|
||||
{quote}
|
||||
</p>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex items-center gap-6 pt-3" style={{ borderTop: '1px solid rgba(26, 24, 19, 0.14)' }}>
|
||||
<div className="flex items-center gap-1.5 text-xs" style={{ color: '#8A8478' }}>
|
||||
<MessageCircle className="w-3.5 h-3.5" />
|
||||
<span>Reply</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-xs" style={{ color: '#8A8478' }}>
|
||||
<Repeat2 className="w-3.5 h-3.5" />
|
||||
<span>Repost</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 text-xs" style={{ color: '#8A8478' }}>
|
||||
<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: '#E0483B' }} />
|
||||
</div>
|
||||
</div>
|
||||
</motion.a>
|
||||
)
|
||||
}
|
||||
|
||||
interface CommunitySectionProps {
|
||||
language?: Language
|
||||
}
|
||||
|
||||
export default function CommunitySection({ language }: CommunitySectionProps) {
|
||||
const tweets: TweetProps[] = []
|
||||
|
||||
return (
|
||||
<section className="py-24 relative" style={{ background: '#F1ECE2' }}>
|
||||
{/* 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(224, 72, 59, 0.08) 0%, transparent 70%)' }}
|
||||
/>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-4 relative z-10">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
className="text-center mb-12"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#1A1813' }}>
|
||||
{language === 'zh' ? 'Community Voices' : 'Community Voices'}
|
||||
</h2>
|
||||
<p className="text-lg" style={{ color: '#8A8478' }}>
|
||||
{language === 'zh' ? 'See what others are saying' : '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://x.com/vergex_ai"
|
||||
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(224, 72, 59, 0.1)',
|
||||
color: '#E0483B',
|
||||
border: '1px solid rgba(224, 72, 59, 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' ? 'Follow us on X' : 'Follow us on X'}
|
||||
</a>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Brain, Swords, BarChart3, Shield, Blocks, LineChart } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
|
||||
interface FeaturesSectionProps {
|
||||
language: Language
|
||||
}
|
||||
|
||||
export default function FeaturesSection({ language }: FeaturesSectionProps) {
|
||||
const features = [
|
||||
{
|
||||
icon: Brain,
|
||||
title: language === 'zh' ? 'AI Strategy Orchestration' : 'AI Strategy Orchestration',
|
||||
desc: language === 'zh'
|
||||
? 'Support DeepSeek, GPT, Claude, Qwen and more. Custom prompts, AI autonomously analyzes markets and makes trading decisions'
|
||||
: 'Support DeepSeek, GPT, Claude, Qwen and more. Custom prompts, AI autonomously analyzes markets and makes trading decisions',
|
||||
highlight: true,
|
||||
badge: language === 'zh' ? 'Core' : 'Core',
|
||||
},
|
||||
{
|
||||
icon: Swords,
|
||||
title: language === 'zh' ? 'Multi-AI Arena' : 'Multi-AI Arena',
|
||||
desc: language === 'zh'
|
||||
? 'Multiple AI traders compete in real-time, live PnL leaderboard, automatic survival of the fittest'
|
||||
: 'Multiple AI traders compete in real-time, live PnL leaderboard, automatic survival of the fittest',
|
||||
highlight: true,
|
||||
badge: language === 'zh' ? 'Unique' : 'Unique',
|
||||
},
|
||||
{
|
||||
icon: LineChart,
|
||||
title: language === 'zh' ? 'Pro Quant Data' : 'Pro Quant Data',
|
||||
desc: language === 'zh'
|
||||
? 'Integrated candlesticks, indicators, order book, funding rates, open interest - comprehensive data for AI decisions'
|
||||
: 'Integrated candlesticks, indicators, order book, funding rates, open interest - comprehensive data for AI decisions',
|
||||
highlight: true,
|
||||
badge: language === 'zh' ? 'Pro' : 'Pro',
|
||||
},
|
||||
{
|
||||
icon: Blocks,
|
||||
title: language === 'zh' ? 'Multi-Exchange Support' : 'Multi-Exchange Support',
|
||||
desc: language === 'zh'
|
||||
? 'Binance, OKX, Bybit, Hyperliquid, Aster DEX - one system, multiple exchanges'
|
||||
: 'Binance, OKX, Bybit, Hyperliquid, Aster DEX - one system, multiple exchanges',
|
||||
},
|
||||
{
|
||||
icon: BarChart3,
|
||||
title: language === 'zh' ? 'Real-time Dashboard' : 'Real-time Dashboard',
|
||||
desc: language === 'zh'
|
||||
? 'Trade monitoring, PnL curves, position analysis, AI decision logs at a glance'
|
||||
: 'Trade monitoring, PnL curves, position analysis, AI decision logs at a glance',
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: language === 'zh' ? 'Open Source & Self-Hosted' : 'Open Source & Self-Hosted',
|
||||
desc: language === 'zh'
|
||||
? 'Fully open source, data stored locally, API keys never leave your server'
|
||||
: 'Fully open source, data stored locally, API keys never leave your server',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="py-24 relative" style={{ background: '#F1ECE2' }}>
|
||||
{/* Background */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.04]"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(#E0483B 1px, transparent 1px), linear-gradient(90deg, #E0483B 1px, transparent 1px)`,
|
||||
backgroundSize: '40px 40px',
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-4 relative z-10">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
className="text-center mb-16"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#1A1813' }}>
|
||||
{t('whyChooseNofx', language)}
|
||||
</h2>
|
||||
<p className="text-lg max-w-2xl mx-auto" style={{ color: '#8A8478' }}>
|
||||
{language === 'zh'
|
||||
? 'Not just a trading bot, but a complete AI trading operating system'
|
||||
: 'Not just a trading bot, but a complete AI trading operating system'}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Features Grid */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-5">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className={`
|
||||
relative group rounded-2xl p-6 transition-all duration-300
|
||||
${feature.highlight ? 'md:col-span-1 lg:col-span-1' : ''}
|
||||
`}
|
||||
style={{
|
||||
background: feature.highlight
|
||||
? 'rgba(224, 72, 59, 0.06)'
|
||||
: '#F7F4EC',
|
||||
border: feature.highlight
|
||||
? '1px solid rgba(224, 72, 59, 0.2)'
|
||||
: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
>
|
||||
{/* Badge */}
|
||||
{feature.badge && (
|
||||
<div
|
||||
className="absolute top-4 right-4 px-2 py-1 rounded text-xs font-medium"
|
||||
style={{
|
||||
background: 'rgba(224, 72, 59, 0.15)',
|
||||
color: '#E0483B',
|
||||
}}
|
||||
>
|
||||
{feature.badge}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Icon */}
|
||||
<motion.div
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center mb-4"
|
||||
style={{
|
||||
background: feature.highlight
|
||||
? 'rgba(224, 72, 59, 0.15)'
|
||||
: 'rgba(224, 72, 59, 0.1)',
|
||||
border: '1px solid rgba(224, 72, 59, 0.2)',
|
||||
}}
|
||||
whileHover={{ scale: 1.1, rotate: 5 }}
|
||||
>
|
||||
<feature.icon
|
||||
className="w-6 h-6"
|
||||
style={{ color: '#E0483B' }}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
{/* Text */}
|
||||
<h3
|
||||
className="text-xl font-bold mb-3"
|
||||
style={{ color: '#1A1813' }}
|
||||
>
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p
|
||||
className="text-sm leading-relaxed"
|
||||
style={{ color: '#8A8478' }}
|
||||
>
|
||||
{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-20 transition-opacity duration-500"
|
||||
style={{ background: '#E0483B' }}
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Bottom Stats */}
|
||||
<motion.div
|
||||
className="mt-16 grid grid-cols-2 md:grid-cols-4 gap-6"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
{[
|
||||
{ value: '10+', label: language === 'zh' ? 'AI Models' : 'AI Models' },
|
||||
{ value: '5+', label: language === 'zh' ? 'Exchanges' : 'Exchanges' },
|
||||
{ value: '24/7', label: language === 'zh' ? 'Auto Trading' : 'Auto Trading' },
|
||||
{ value: '100%', label: language === 'zh' ? 'Open Source' : 'Open Source' },
|
||||
].map((stat) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className="text-center p-4 rounded-xl"
|
||||
style={{
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="text-2xl font-bold mb-1"
|
||||
style={{
|
||||
color: '#E0483B',
|
||||
}}
|
||||
>
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-xs" style={{ color: '#8A8478' }}>
|
||||
{stat.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowRight, Play, Github, Zap } from 'lucide-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
import { useGitHubStats } from '../../hooks/useGitHubStats'
|
||||
import { useCounterAnimation } from '../../hooks/useCounterAnimation'
|
||||
import { OFFICIAL_LINKS } from '../../constants/branding'
|
||||
|
||||
interface HeroSectionProps {
|
||||
language: Language
|
||||
}
|
||||
|
||||
export default function HeroSection({ language }: HeroSectionProps) {
|
||||
const { stars, daysOld, isLoading } = useGitHubStats('NoFxAiOS', 'nofx')
|
||||
const animatedStars = useCounterAnimation({
|
||||
start: 0,
|
||||
end: stars,
|
||||
duration: 2000,
|
||||
})
|
||||
|
||||
return (
|
||||
<section className="relative min-h-screen flex items-center justify-center overflow-hidden pt-16">
|
||||
{/* Animated Background */}
|
||||
<div className="absolute inset-0 overflow-hidden">
|
||||
{/* Grid Pattern */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.03]"
|
||||
style={{
|
||||
backgroundImage: `linear-gradient(#E0483B 1px, transparent 1px), linear-gradient(90deg, #E0483B 1px, transparent 1px)`,
|
||||
backgroundSize: '60px 60px',
|
||||
}}
|
||||
/>
|
||||
{/* Radial Gradient */}
|
||||
<div
|
||||
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] rounded-full"
|
||||
style={{
|
||||
background:
|
||||
'radial-gradient(circle, rgba(224, 72, 59, 0.08) 0%, transparent 70%)',
|
||||
}}
|
||||
/>
|
||||
{/* Floating Orbs */}
|
||||
<motion.div
|
||||
className="absolute top-20 right-20 w-32 h-32 rounded-full blur-3xl"
|
||||
style={{ background: 'rgba(224, 72, 59, 0.12)' }}
|
||||
animate={{
|
||||
y: [0, 30, 0],
|
||||
scale: [1, 1.1, 1],
|
||||
}}
|
||||
transition={{ duration: 8, repeat: Infinity, ease: 'easeInOut' }}
|
||||
/>
|
||||
<motion.div
|
||||
className="absolute bottom-40 left-20 w-48 h-48 rounded-full blur-3xl"
|
||||
style={{ background: 'rgba(224, 72, 59, 0.08)' }}
|
||||
animate={{
|
||||
y: [0, -40, 0],
|
||||
scale: [1, 1.2, 1],
|
||||
}}
|
||||
transition={{ duration: 10, repeat: Infinity, ease: 'easeInOut' }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10 max-w-6xl mx-auto px-4 text-center">
|
||||
{/* Badge */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="inline-flex items-center gap-2 px-4 py-2 rounded-full mb-8"
|
||||
style={{
|
||||
background: 'rgba(224, 72, 59, 0.1)',
|
||||
border: '1px solid rgba(224, 72, 59, 0.3)',
|
||||
}}
|
||||
>
|
||||
<Zap className="w-4 h-4" style={{ color: '#E0483B' }} />
|
||||
<span className="text-sm font-medium" style={{ color: '#E0483B' }}>
|
||||
{isLoading ? (
|
||||
t('githubStarsInDays', language)
|
||||
) : language === 'zh' ? (
|
||||
<>
|
||||
<span className="font-bold tabular-nums">
|
||||
{(animatedStars / 1000).toFixed(1)}K+
|
||||
</span>{' '}
|
||||
GitHub Stars in {daysOld} days
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<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: '#1A1813' }}>{t('heroTitle1', language)}</span>
|
||||
<br />
|
||||
<span
|
||||
className="relative inline-block"
|
||||
style={{
|
||||
color: '#E0483B',
|
||||
}}
|
||||
>
|
||||
{t('heroTitle2', language)}
|
||||
<motion.span
|
||||
className="absolute -bottom-2 left-0 h-1 rounded-full"
|
||||
style={{ background: '#E0483B' }}
|
||||
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: '#8A8478' }}
|
||||
>
|
||||
{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.div
|
||||
className="group flex items-center gap-3 px-8 py-4 rounded-xl font-bold text-lg transition-all"
|
||||
style={{
|
||||
background: '#E0483B',
|
||||
color: '#F7F4EC',
|
||||
boxShadow: '0 4px 24px rgba(224, 72, 59, 0.25)',
|
||||
}}
|
||||
whileHover={{
|
||||
scale: 1.02,
|
||||
boxShadow: '0 8px 32px rgba(224, 72, 59, 0.35)',
|
||||
}}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
>
|
||||
<Link to="/competition" className="flex items-center gap-3">
|
||||
<Play className="w-5 h-5" />
|
||||
{t('liveCompetition', language) || 'Live Competition'}
|
||||
<ArrowRight className="w-5 h-5 transition-transform group-hover:translate-x-1" />
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
||||
<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: '#F7F4EC',
|
||||
color: '#1A1813',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
whileHover={{
|
||||
scale: 1.02,
|
||||
background: '#F2EEE4',
|
||||
borderColor: 'rgba(224, 72, 59, 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' : 'Exchanges',
|
||||
value: '5+',
|
||||
},
|
||||
{
|
||||
label: language === 'zh' ? 'AI Models' : 'AI Models',
|
||||
value: '10+',
|
||||
},
|
||||
{
|
||||
label: language === 'zh' ? 'Open Source' : '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={{
|
||||
color: '#E0483B',
|
||||
}}
|
||||
>
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-sm" style={{ color: '#8A8478' }}>
|
||||
{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: '#8A8478' }}
|
||||
>
|
||||
{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(224, 72, 59, 0.3)' }}
|
||||
animate={{ y: [0, 8, 0] }}
|
||||
transition={{ duration: 2, repeat: Infinity }}
|
||||
>
|
||||
<motion.div
|
||||
className="w-1.5 h-3 rounded-full"
|
||||
style={{ background: '#E0483B' }}
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { Download, Rocket, TrendingUp, AlertTriangle } from 'lucide-react'
|
||||
import { t, Language } from '../../i18n/translations'
|
||||
|
||||
interface HowItWorksSectionProps {
|
||||
language: Language
|
||||
}
|
||||
|
||||
export default function HowItWorksSection({ language }: HowItWorksSectionProps) {
|
||||
const steps = [
|
||||
{
|
||||
icon: Download,
|
||||
number: '01',
|
||||
title: language === 'zh' ? 'One-Click Deploy' : 'One-Click Deploy',
|
||||
desc: language === 'zh'
|
||||
? 'Run a single command on your server to deploy'
|
||||
: 'Run a single command on your server to deploy',
|
||||
code: 'curl -fsSL https://raw.githubusercontent.com/NoFxAiOS/nofx/main/install.sh | bash',
|
||||
},
|
||||
{
|
||||
icon: Rocket,
|
||||
number: '02',
|
||||
title: language === 'zh' ? 'Access Dashboard' : 'Access Dashboard',
|
||||
desc: language === 'zh'
|
||||
? 'Access your server via browser'
|
||||
: 'Access your server via browser',
|
||||
code: 'http://YOUR_SERVER_IP:3000',
|
||||
},
|
||||
{
|
||||
icon: TrendingUp,
|
||||
number: '03',
|
||||
title: language === 'zh' ? 'Start Trading' : 'Start Trading',
|
||||
desc: language === 'zh'
|
||||
? 'Create trader, let AI do the work'
|
||||
: 'Create trader, let AI do the work',
|
||||
code: language === 'zh' ? 'Configure Model → Exchange → Create Trader' : 'Configure Model → Exchange → Create Trader',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="py-24 relative overflow-hidden" style={{ background: '#F1ECE2' }}>
|
||||
{/* 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(224, 72, 59, 0.12) 0%, transparent 70%)' }}
|
||||
/>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-4 relative z-10">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
className="text-center mb-16"
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="text-4xl lg:text-5xl font-bold mb-4" style={{ color: '#1A1813' }}>
|
||||
{t('howToStart', language)}
|
||||
</h2>
|
||||
<p className="text-lg" style={{ color: '#8A8478' }}>
|
||||
{t('fourSimpleSteps', language)}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Steps Timeline */}
|
||||
<div className="relative">
|
||||
{/* Connecting Line */}
|
||||
<div
|
||||
className="absolute left-[39px] top-0 bottom-0 w-px hidden lg:block"
|
||||
style={{ background: 'linear-gradient(to bottom, transparent, rgba(224, 72, 59, 0.3), transparent)' }}
|
||||
/>
|
||||
|
||||
<div className="space-y-6">
|
||||
{steps.map((step, index) => (
|
||||
<motion.div
|
||||
key={step.number}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: index * 0.15 }}
|
||||
className="relative"
|
||||
>
|
||||
<div
|
||||
className="flex flex-col lg:flex-row items-start gap-6 p-6 rounded-2xl transition-all duration-300 hover:translate-x-2"
|
||||
style={{
|
||||
background: '#F7F4EC',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
>
|
||||
{/* 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(224, 72, 59, 0.15) 0%, rgba(224, 72, 59, 0.05) 100%)',
|
||||
border: '1px solid rgba(224, 72, 59, 0.3)',
|
||||
}}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
>
|
||||
<step.icon className="w-8 h-8" style={{ color: '#E0483B' }} />
|
||||
</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: '#E0483B' }}
|
||||
>
|
||||
{step.number}
|
||||
</span>
|
||||
<h3 className="text-xl font-bold" style={{ color: '#1A1813' }}>
|
||||
{step.title}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-4" style={{ color: '#8A8478' }}>
|
||||
{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: '#E8E2D5',
|
||||
border: '1px solid rgba(26, 24, 19, 0.14)',
|
||||
}}
|
||||
>
|
||||
<span style={{ color: '#8A8478' }}>$</span>
|
||||
<span style={{ color: '#1A1813' }}>{step.code}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Risk Warning */}
|
||||
<motion.div
|
||||
className="mt-12 p-6 rounded-2xl flex items-start gap-4"
|
||||
style={{
|
||||
background: 'rgba(224, 72, 59, 0.05)',
|
||||
border: '1px solid rgba(224, 72, 59, 0.15)',
|
||||
}}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<div
|
||||
className="w-12 h-12 rounded-xl flex items-center justify-center flex-shrink-0"
|
||||
style={{ background: 'rgba(224, 72, 59, 0.1)' }}
|
||||
>
|
||||
<AlertTriangle className="w-6 h-6" style={{ color: '#E0483B' }} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-semibold mb-2" style={{ color: '#E0483B' }}>
|
||||
{t('importantRiskWarning', language)}
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed" style={{ color: '#8A8478' }}>
|
||||
{t('riskWarningText', language)}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -31,14 +31,33 @@ export default function DeploymentHub() {
|
||||
</h2>
|
||||
|
||||
<p className="text-nofx-text-muted text-lg leading-relaxed font-light">
|
||||
Initialize your own high-frequency trading node in seconds.
|
||||
Our optimized installer handles all dependencies, bringing the trading system online with a single command.
|
||||
One command on your laptop or any server installs
|
||||
everything. Open the address it prints, create your
|
||||
account, and the guided launch takes you to your
|
||||
first AI trade in about five minutes — around $13
|
||||
is enough to start.
|
||||
</p>
|
||||
|
||||
{/* the first five minutes, in plain words */}
|
||||
<ol className="space-y-2 pt-2 font-mono text-sm text-nofx-text-muted">
|
||||
{[
|
||||
'Register — the first account owns this instance.',
|
||||
'Fund two small wallets: $1+ for AI fees, $12+ to trade with (guided, with QR codes).',
|
||||
'Press Start — the AI trades on its own; stop it anytime.',
|
||||
].map((step, i) => (
|
||||
<li key={i} className="flex gap-3">
|
||||
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded border border-nofx-gold/30 bg-nofx-gold/10 text-[11px] font-bold text-nofx-gold">
|
||||
{i + 1}
|
||||
</span>
|
||||
<span>{step}</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 pt-4">
|
||||
{[
|
||||
{ icon: Command, label: "One-Line Install", desc: "No configuration needed" },
|
||||
{ icon: Shield, label: "Secure Core", desc: "Sandboxed execution env" }
|
||||
{ icon: Command, label: "One-Line Install", desc: "Docker handles every dependency" },
|
||||
{ icon: Shield, label: "Your Keys Stay Home", desc: "Runs on your machine, keys encrypted locally" }
|
||||
].map((item, i) => (
|
||||
<div key={i} className="flex gap-4 items-start p-4 rounded bg-nofx-bg-lighter border border-[rgba(26,24,19,0.14)] hover:border-nofx-gold/30 transition-colors group">
|
||||
<div className="p-2 rounded bg-nofx-bg-deeper border border-[rgba(26,24,19,0.14)] text-nofx-gold group-hover:bg-nofx-gold/10 transition-colors">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowRight, Shield, Activity, CircuitBoard, Wifi, Globe, Zap, Star, GitFork, Users, MessageCircle } from 'lucide-react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useGitHubStats } from '../../../hooks/useGitHubStats'
|
||||
import AgentTerminal from '../brand/AgentTerminal'
|
||||
|
||||
@@ -16,6 +17,7 @@ const tickerLabels: Record<string, string> = {
|
||||
}
|
||||
|
||||
export default function TerminalHero() {
|
||||
const navigate = useNavigate()
|
||||
|
||||
// Real-time price state
|
||||
const [prices, setPrices] = useState<Record<string, string>>({
|
||||
@@ -231,17 +233,29 @@ export default function TerminalHero() {
|
||||
{/* CTA Buttons */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 w-full justify-center">
|
||||
<button
|
||||
onClick={() => document.getElementById('market-scanner')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
onClick={() => navigate('/traders')}
|
||||
className="group relative overflow-hidden bg-nofx-gold text-nofx-bg-lighter px-8 py-4 font-bold font-mono tracking-wider hover:scale-105 transition-transform duration-200"
|
||||
style={{ clipPath: 'polygon(10% 0, 100% 0, 100% 70%, 90% 100%, 0 100%, 0 30%)' }}
|
||||
>
|
||||
<span className="relative z-10 flex items-center gap-2">
|
||||
CREATE STOCK TRADER <ArrowRight className="w-4 h-4" />
|
||||
START THE AUTOPILOT <ArrowRight className="w-4 h-4" />
|
||||
</span>
|
||||
<div className="absolute inset-0 bg-nofx-text/10 translate-y-full group-hover:translate-y-0 transition-transform duration-300"></div>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => document.getElementById('market-scanner')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="px-8 py-4 font-bold font-mono tracking-wider text-nofx-text border border-[rgba(26,24,19,0.2)] rounded hover:border-nofx-gold/50 hover:text-nofx-gold transition-colors"
|
||||
>
|
||||
SEE IT WORK
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Plain-language promise — the anti-jargon line */}
|
||||
<p className="mt-5 text-sm text-nofx-text-muted font-mono">
|
||||
Self-hosted & open source · about $13 is enough to start · guided
|
||||
setup, no API keys — first trade in minutes
|
||||
</p>
|
||||
|
||||
{/* Community Stats Row */}
|
||||
<CommunityStats />
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import useSWR from 'swr'
|
||||
import { api } from '../../lib/api'
|
||||
import { ApiError } from '../../lib/httpClient'
|
||||
import { ROUTES } from '../../router/paths'
|
||||
import type {
|
||||
TraderInfo,
|
||||
CreateTraderRequest,
|
||||
@@ -667,8 +668,9 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
||||
if (!setupTarget) return
|
||||
|
||||
if (setupTarget === 'claw402') {
|
||||
if (supportedModels.length === 0 && allModels.length === 0) return
|
||||
handleOpenClaw402Config()
|
||||
// The welcome page shows the deposit QR, auto-creates the wallet if
|
||||
// needed, and polls the balance — friendlier than the key-config modal.
|
||||
navigate(ROUTES.welcome)
|
||||
} else if (setupTarget === 'hyperliquid') {
|
||||
handleOpenHyperliquidConfig()
|
||||
} else if (setupTarget === 'hyperliquid-funds') {
|
||||
@@ -693,6 +695,21 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
||||
await Promise.all([loadConfigs(), mutateTraders()])
|
||||
}
|
||||
|
||||
const hasRunningTrader = (traders || []).some((trader) => trader.is_running)
|
||||
const launchPanel = (
|
||||
<AutopilotLaunchPanel
|
||||
models={allModels}
|
||||
exchanges={allExchanges}
|
||||
exchangeAccountStates={exchangeAccountStates}
|
||||
traders={traders || []}
|
||||
isLoggedIn={Boolean(user && token)}
|
||||
language={language}
|
||||
onRefresh={refreshLaunchState}
|
||||
onOpenClaw402Config={handleOpenClaw402Config}
|
||||
onOpenHyperliquidConfig={handleOpenHyperliquidConfig}
|
||||
/>
|
||||
)
|
||||
|
||||
return (
|
||||
<DeepVoidBackground className="py-8" disableAnimation>
|
||||
<div className="w-full px-4 md:px-8 space-y-8 animate-fade-in">
|
||||
@@ -767,6 +784,10 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Newcomers see the guided launch FIRST — the config grid is detail.
|
||||
Once an autopilot is running, the running bot takes back the top. */}
|
||||
{!hasRunningTrader && launchPanel}
|
||||
|
||||
{/* Configuration Status Grid */}
|
||||
<ConfigStatusGrid
|
||||
configuredModels={configuredModels}
|
||||
@@ -784,17 +805,7 @@ export function AITradersPage({ onTraderSelect }: AITradersPageProps) {
|
||||
onCopyAddress={handleCopyAddress}
|
||||
/>
|
||||
|
||||
<AutopilotLaunchPanel
|
||||
models={allModels}
|
||||
exchanges={allExchanges}
|
||||
exchangeAccountStates={exchangeAccountStates}
|
||||
traders={traders || []}
|
||||
isLoggedIn={Boolean(user && token)}
|
||||
language={language}
|
||||
onRefresh={refreshLaunchState}
|
||||
onOpenClaw402Config={handleOpenClaw402Config}
|
||||
onOpenHyperliquidConfig={handleOpenHyperliquidConfig}
|
||||
/>
|
||||
{hasRunningTrader && launchPanel}
|
||||
|
||||
{/* Traders List */}
|
||||
<TradersList
|
||||
|
||||
@@ -394,20 +394,20 @@ export function AutopilotLaunchPanel({
|
||||
action?: JSX.Element
|
||||
}> = [
|
||||
{
|
||||
title: 'AI fee wallet',
|
||||
title: 'Step 1 · Fund the AI wallet ($1+)',
|
||||
detail:
|
||||
'Pays Claw402.ai data and model calls with Base USDC. This is separate from trading collateral.',
|
||||
'The AI pays a tiny fee each time it thinks. Send $1 or more of USDC on the Base network to this address — from Binance, OKX, Coinbase or any wallet. Separate from your trading money.',
|
||||
status: feeReady ? 'ready' : 'action',
|
||||
meta: feeWalletAddress
|
||||
? `${shortAddress(feeWalletAddress)} · ${formatUSDC(feeWalletBalance)} USDC${
|
||||
feeReady ? '' : ` · needs ≥ ${minAIFeeUSDC} USDC`
|
||||
}`
|
||||
: 'Base USDC wallet required',
|
||||
: 'Takes 1 minute — we create the wallet for you',
|
||||
action: feeWalletAddress ? (
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenClaw402Config?.()}
|
||||
onClick={() => navigate(ROUTES.welcome)}
|
||||
className="inline-flex items-center gap-1.5 text-xs font-semibold text-nofx-gold hover:text-nofx-accent"
|
||||
>
|
||||
<CircleDollarSign className="h-3.5 w-3.5" />
|
||||
@@ -425,22 +425,22 @@ export function AutopilotLaunchPanel({
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onOpenClaw402Config?.()}
|
||||
onClick={() => navigate(ROUTES.welcome)}
|
||||
className="inline-flex items-center gap-1.5 text-xs font-semibold text-nofx-gold hover:text-nofx-accent"
|
||||
>
|
||||
<ArrowRight className="h-3.5 w-3.5" />
|
||||
Open
|
||||
Create
|
||||
</button>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Hyperliquid trading wallet',
|
||||
title: 'Step 2 · Connect Hyperliquid',
|
||||
detail:
|
||||
'Connect an EVM wallet, approve a NOFX Agent, approve the builder fee, then save it to NOFX.',
|
||||
'Approve NOFX once with your crypto wallet (Rabby or MetaMask). This lets the AI place trades for you — it can never withdraw your money.',
|
||||
status: hyperliquidConnected ? 'ready' : 'action',
|
||||
meta: hyperliquidExchange?.hyperliquidWalletAddr
|
||||
? `${shortAddress(hyperliquidExchange.hyperliquidWalletAddr)} · authorized`
|
||||
: 'Agent and trading authorization required',
|
||||
: 'A few clicks + 3 wallet signatures',
|
||||
action: (
|
||||
<button
|
||||
type="button"
|
||||
@@ -453,9 +453,9 @@ export function AutopilotLaunchPanel({
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Trading balance',
|
||||
title: 'Step 3 · Add trading money ($12+)',
|
||||
detail:
|
||||
'Deposit USDC to Hyperliquid. NOFX uses it as margin for the Claw402 Autopilot strategy.',
|
||||
'Deposit USDC into your Hyperliquid account. This is what the AI trades with — start small, you can add more anytime.',
|
||||
status: tradingBalanceReady
|
||||
? 'ready'
|
||||
: hyperliquidConnected
|
||||
@@ -465,18 +465,20 @@ export function AutopilotLaunchPanel({
|
||||
? `${formatUSDC(tradingBalance)} USDC available${
|
||||
tradingBalanceReady ? '' : ` · needs ≥ ${minTradingUSDC} USDC`
|
||||
}`
|
||||
: 'Connect Hyperliquid first',
|
||||
: 'Finish step 2 first',
|
||||
},
|
||||
{
|
||||
title: 'NOFX Autopilot',
|
||||
title: 'Step 4 · Press start',
|
||||
detail:
|
||||
'Reads the Claw402 board, fetches Signal Lab and liquidation structure, confirms with candles, then trades full-size 10x only when the setup is strong enough.',
|
||||
'The AI reads the market every few minutes, picks its trades, and manages them on its own. Watch every decision live on the dashboard — stop it with one click anytime.',
|
||||
status: allReady ? 'ready' : 'blocked',
|
||||
meta: autopilotTrader?.is_running
|
||||
? 'Running'
|
||||
? 'Running — open the dashboard to watch'
|
||||
: autopilotTrader
|
||||
? 'Ready to start'
|
||||
: 'Ready to create when setup is complete',
|
||||
: allReady
|
||||
? 'Everything is ready — press the button'
|
||||
: 'Unlocks when steps 1–3 are green',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -485,16 +487,10 @@ export function AutopilotLaunchPanel({
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (onOpenClaw402Config) {
|
||||
onOpenClaw402Config()
|
||||
} else {
|
||||
navigate(ROUTES.welcome)
|
||||
}
|
||||
}}
|
||||
onClick={() => navigate(ROUTES.welcome)}
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-nofx-gold px-4 py-3 text-sm font-bold text-white hover:bg-nofx-accent"
|
||||
>
|
||||
Open Claw402 wallet
|
||||
Set up the AI wallet
|
||||
<ArrowRight className="h-4 w-4" />
|
||||
</button>
|
||||
)
|
||||
@@ -584,8 +580,8 @@ export function AutopilotLaunchPanel({
|
||||
Start NOFX Autopilot in minutes
|
||||
</h2>
|
||||
<p className="mt-2 max-w-2xl text-sm leading-6 text-nofx-text-muted">
|
||||
One strategy, one launch path. Fund the AI fee wallet, authorize
|
||||
Hyperliquid, deposit USDC, then run the Claw402 Autopilot.
|
||||
Four small steps, about $13 total. No API keys, no config
|
||||
files — the AI trades for you, and you can stop it anytime.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
|
||||
Reference in New Issue
Block a user