mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-28 22:42:52 +08:00
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.
372 lines
21 KiB
TypeScript
372 lines
21 KiB
TypeScript
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'
|
|
|
|
const tickerLabels: Record<string, string> = {
|
|
AAPL: 'AAPL',
|
|
MSFT: 'MSFT',
|
|
NVDA: 'NVDA',
|
|
TSLA: 'TSLA',
|
|
GOLD: 'GOLD',
|
|
EURUSD: 'EUR/USD',
|
|
SPX: 'S&P 500',
|
|
PREIPO: 'PRE-IPO INDEX',
|
|
}
|
|
|
|
export default function TerminalHero() {
|
|
const navigate = useNavigate()
|
|
|
|
// Real-time price state
|
|
const [prices, setPrices] = useState<Record<string, string>>({
|
|
AAPL: '...',
|
|
MSFT: '...',
|
|
NVDA: '...',
|
|
TSLA: '...',
|
|
GOLD: '...',
|
|
EURUSD: '...',
|
|
SPX: '...',
|
|
PREIPO: '...'
|
|
})
|
|
|
|
useEffect(() => {
|
|
const fetchPrices = async () => {
|
|
const symbols = ['AAPL', 'MSFT', 'NVDA', 'TSLA', 'GOLD', 'EURUSD', 'SPX']
|
|
|
|
// We use Promise.all to fetch them in parallel for now, or sequentially if rate limited.
|
|
// Parallel is better for UI responsiveness.
|
|
try {
|
|
const results = await Promise.all(symbols.map(async (sym) => {
|
|
try {
|
|
// Use native fetch to bypass global error handlers (toasts) in httpClient
|
|
const response = await fetch(`/api/klines?symbol=xyz:${sym}&interval=1m&limit=1`)
|
|
if (!response.ok) return null
|
|
|
|
const res = await response.json()
|
|
// Check for standard API response structure or direct array
|
|
const klineData = res.data || res
|
|
|
|
if (Array.isArray(klineData) && klineData.length > 0) {
|
|
const closePrice = parseFloat(klineData[0].close || klineData[0][4]) // Handle object or array format
|
|
if (isNaN(closePrice)) return null
|
|
|
|
// Format price: < 1 use 4 decimals, > 1 use 2
|
|
const formatted = closePrice < 1
|
|
? closePrice.toFixed(4)
|
|
: closePrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
|
return { symbol: sym, price: formatted }
|
|
}
|
|
} catch (err) {
|
|
// Silent failure for background polling
|
|
}
|
|
return null
|
|
}))
|
|
|
|
const newPrices: Record<string, string> = {}
|
|
results.forEach(r => {
|
|
if (r) newPrices[r.symbol] = r.price
|
|
})
|
|
|
|
setPrices(prev => ({ ...prev, ...newPrices }))
|
|
|
|
} catch (e) {
|
|
console.error("Failed to fetch market prices", e)
|
|
}
|
|
}
|
|
|
|
// Only fetch once on mount, cache the result
|
|
fetchPrices()
|
|
}, [])
|
|
|
|
return (
|
|
<section className="relative w-full min-h-screen bg-nofx-bg text-nofx-text overflow-hidden flex flex-col pt-20">
|
|
|
|
{/* BACKGROUND LAYERS */}
|
|
{/* 1. Grid */}
|
|
<div className="absolute inset-0 bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-10 mix-blend-multiply pointer-events-none"></div>
|
|
<div className="absolute inset-x-0 bottom-0 h-[50vh] bg-[linear-gradient(to_right,#1a181310_1px,transparent_1px),linear-gradient(to_bottom,#1a181310_1px,transparent_1px)] bg-[size:40px_40px] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)] pointer-events-none md:hidden" style={{ transform: 'perspective(500px) rotateX(60deg) translateY(100px) scale(2)' }}></div>
|
|
<div className="absolute inset-0 bg-grid-pattern opacity-[0.03] pointer-events-none"></div>
|
|
|
|
{/* 2. World Map / Data Viz Background (Abstract) */}
|
|
<div className="absolute inset-0 flex items-center justify-center opacity-10 pointer-events-none">
|
|
<div className="w-[80vw] h-[80vw] rounded-full border border-nofx-gold/20 animate-pulse-slow"></div>
|
|
<div className="absolute w-[60vw] h-[60vw] rounded-full border border-dashed border-nofx-accent/20 animate-[spin_60s_linear_infinite]"></div>
|
|
</div>
|
|
|
|
{/* 3. Gradient Spots - Intensified for Mobile */}
|
|
<div className="absolute top-[-10%] left-[-10%] w-[40vw] h-[40vw] bg-nofx-gold/10 rounded-full blur-[120px] pointer-events-none"></div>
|
|
<div className="absolute bottom-[-10%] right-[-10%] w-[40vw] h-[40vw] bg-nofx-accent/10 rounded-full blur-[120px] pointer-events-none"></div>
|
|
|
|
{/* Mobile Bottom Fade */}
|
|
<div className="absolute bottom-0 left-0 w-full h-32 bg-gradient-to-t from-nofx-bg to-transparent z-20 pointer-events-none md:hidden" />
|
|
|
|
{/* Mobile Floating HUD */}
|
|
<div className="md:hidden absolute top-24 right-4 z-0 opacity-30 pointer-events-none">
|
|
<div className="w-20 h-20 border border-dashed border-nofx-gold/30 rounded-full animate-spin-slow flex items-center justify-center">
|
|
<div className="w-12 h-12 border border-nofx-accent/30 rounded-full"></div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* CONTENT GRID */}
|
|
<div className="relative z-10 flex-1 grid grid-cols-1 lg:grid-cols-12 gap-0 lg:gap-8 max-w-[1800px] mx-auto w-full px-6 h-full pb-20 pt-10 pointer-events-none">
|
|
|
|
{/* LEFT COLUMN: TELEMETRY & STATUS */}
|
|
<div className="hidden lg:flex col-span-3 flex-col justify-between h-full border-r border-[rgba(26,24,19,0.14)] pr-8 py-10 pointer-events-auto">
|
|
|
|
{/* Top: System Health */}
|
|
<div className="space-y-6">
|
|
<div className="border border-[rgba(26,24,19,0.14)] rounded p-4 bg-nofx-bg-lighter">
|
|
<h3 className="text-xs font-mono text-nofx-gold mb-4 flex items-center gap-2">
|
|
<Activity className="w-3 h-3" /> SYSTEM_DIAGNOSTICS
|
|
</h3>
|
|
<div className="space-y-3 font-mono text-[10px] text-nofx-text-muted">
|
|
<div className="flex justify-between items-center">
|
|
<span>KERNEL_LATENCY</span>
|
|
<span className="text-nofx-accent">12ms</span>
|
|
</div>
|
|
<div className="w-full h-1 bg-nofx-bg-deeper rounded-full overflow-hidden">
|
|
<div className="w-[90%] h-full bg-nofx-accent/50"></div>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center">
|
|
<span>MEMORY_INTEGRITY</span>
|
|
<span className="text-nofx-success">100%</span>
|
|
</div>
|
|
<div className="w-full h-1 bg-nofx-bg-deeper rounded-full overflow-hidden">
|
|
<div className="w-full h-full bg-nofx-success/50"></div>
|
|
</div>
|
|
|
|
<div className="flex justify-between items-center">
|
|
<span>UPTIME</span>
|
|
<span className="text-nofx-text">99.999%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-4 border border-[rgba(26,24,19,0.14)] rounded bg-nofx-bg-lighter">
|
|
<div className="flex items-center gap-3 text-nofx-text-muted mb-2">
|
|
<Shield className="w-4 h-4" />
|
|
<span className="text-[10px] font-mono tracking-widest">SECURITY PROTOCOLS</span>
|
|
</div>
|
|
<div className="flex gap-1">
|
|
<div className="h-1 flex-1 bg-nofx-gold"></div>
|
|
<div className="h-1 flex-1 bg-nofx-gold"></div>
|
|
<div className="h-1 flex-1 bg-nofx-gold"></div>
|
|
<div className="h-1 flex-1 bg-nofx-bg-deeper"></div>
|
|
</div>
|
|
<div className="mt-2 text-right text-[10px] text-nofx-gold/80 font-mono">LEVEL 3 ACTIVATE</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Bottom: Network Log */}
|
|
<div className="font-mono text-[10px] text-nofx-text-muted space-y-1 opacity-70">
|
|
<div>> CONNECTING TO MARKET DATA... OK</div>
|
|
<div>> SYNCING VENUES (424/424)... OK</div>
|
|
<div>> LOADING MULTI-ASSET UNIVERSE... DONE</div>
|
|
<div className="animate-pulse">> AWAITING USER INPUT_</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* CENTER COLUMN: MAIN ACTION */}
|
|
<div className="col-span-1 lg:col-span-6 flex flex-col items-center justify-center text-center relative z-20 pointer-events-auto">
|
|
|
|
{/* Project Identity Chip */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: -20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
className="mb-8 inline-flex items-center gap-3 px-4 py-2 rounded-full border border-nofx-gold/20 bg-nofx-gold/5 backdrop-blur-md"
|
|
>
|
|
<span className="relative flex h-2 w-2">
|
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-nofx-gold opacity-75"></span>
|
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-nofx-gold"></span>
|
|
</span>
|
|
<span className="text-xs font-mono text-nofx-gold tracking-widest">NOFX PROFESSIONAL MULTI-ASSET AGENT OS</span>
|
|
</motion.div>
|
|
|
|
{/* Main Title - Massive & Impactful */}
|
|
{/* Main Title - Massive & Impactful */}
|
|
<div className="relative z-20">
|
|
<h1 className="text-5xl sm:text-6xl md:text-8xl lg:text-9xl font-black tracking-tighter leading-[0.9] md:leading-[0.8] mb-6 select-none text-nofx-text">
|
|
AGENTIC<br />
|
|
<span className="text-nofx-gold animate-shimmer tracking-tight">TRADING</span>
|
|
</h1>
|
|
|
|
<p className="max-w-xl text-nofx-text-muted text-lg mb-6 font-light leading-relaxed">
|
|
Professional AI trading agents for US stocks, commodities, FX and Pre-IPO synthetic markets.
|
|
Build institutional-grade strategies by chatting in plain English.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Market Access Strip - Prominent Display */}
|
|
{/* Market Access Strip - Prominent Display */}
|
|
<div className="flex flex-col gap-4 mb-14">
|
|
<div className="text-nofx-gold/80 font-mono text-xs tracking-[0.3em] uppercase flex items-center gap-2 ml-1">
|
|
<span className="relative flex h-2 w-2">
|
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-nofx-success opacity-75"></span>
|
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-nofx-success"></span>
|
|
</span>
|
|
Live Data Feeds Active
|
|
</div>
|
|
<div className="flex flex-wrap gap-4 font-mono">
|
|
{['US STOCKS', 'COMMODITIES', 'FOREX', 'PRE-IPO'].map((market) => (
|
|
<div key={market} className="relative group cursor-default">
|
|
<div className="absolute -inset-0.5 bg-nofx-gold/15 rounded-lg blur opacity-0 group-hover:opacity-100 transition duration-500"></div>
|
|
<div className="relative flex items-center gap-3 px-6 py-3 rounded-lg bg-nofx-bg-lighter border border-[rgba(26,24,19,0.14)] hover:border-nofx-gold/50 transition-all duration-300">
|
|
<div className="w-1.5 h-1.5 rounded-full bg-nofx-success animate-pulse"></div>
|
|
<span className="text-lg md:text-xl font-bold text-nofx-text tracking-wider group-hover:text-nofx-gold transition-colors">{market}</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Command Line Input Simulation */}
|
|
<div className="w-full max-w-lg h-12 bg-nofx-bg-lighter border border-[rgba(26,24,19,0.14)] rounded flex items-center px-4 mb-10 font-mono text-sm shadow-sm group hover:border-nofx-gold/50 transition-colors cursor-text" onClick={() => document.getElementById('market-scanner')?.scrollIntoView({ behavior: 'smooth' })}>
|
|
<span className="text-nofx-success mr-2">➜</span>
|
|
<span className="text-nofx-accent mr-2">~</span>
|
|
<span className="text-nofx-text-muted">create US stock trader --idea="breakouts"</span>
|
|
<span className="w-2 h-4 bg-nofx-gold ml-1 animate-pulse"></span>
|
|
</div>
|
|
|
|
{/* CTA Buttons */}
|
|
<div className="flex flex-col sm:flex-row gap-4 w-full justify-center">
|
|
<button
|
|
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">
|
|
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 />
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{/* RIGHT COLUMN: Trader Terminal - Desktop Only */}
|
|
<div className="absolute top-0 right-0 h-full w-[50vw] hidden lg:flex flex-col items-end justify-end pr-8 pb-20 z-10">
|
|
{/* Subtle gradient orb */}
|
|
<div className="absolute top-1/2 right-[10%] -translate-y-1/2 w-[400px] h-[400px] rounded-full bg-nofx-gold/8 blur-[100px] pointer-events-none"></div>
|
|
|
|
{/* Subtle grid fade */}
|
|
<div
|
|
className="absolute inset-0 opacity-[0.03] pointer-events-none"
|
|
style={{
|
|
backgroundImage: 'radial-gradient(circle at 1px 1px, rgba(26,24,19,0.3) 1px, transparent 0)',
|
|
backgroundSize: '40px 40px',
|
|
maskImage: 'radial-gradient(ellipse 80% 80% at 70% 50%, black 20%, transparent 70%)',
|
|
WebkitMaskImage: 'radial-gradient(ellipse 80% 80% at 70% 50%, black 20%, transparent 70%)'
|
|
}}
|
|
></div>
|
|
|
|
{/* Trader Terminal Panel */}
|
|
<div className="relative z-20 pointer-events-auto">
|
|
<AgentTerminal />
|
|
</div>
|
|
</div>
|
|
|
|
{/* FLOATING TICKER FOOTER */}
|
|
<div className="absolute bottom-0 w-full bg-nofx-bg-lighter border-t border-[rgba(26,24,19,0.14)] backdrop-blur-md z-30 overflow-hidden py-2 flex items-center">
|
|
<div className="flex animate-marquee whitespace-nowrap gap-12 text-xs font-mono text-nofx-text-muted px-4">
|
|
<span className="flex items-center gap-2"><Globe className="w-3 h-3 text-nofx-text-muted" /> GLOBAL MARKET ACCESS</span>
|
|
<span className="flex items-center gap-2 text-nofx-gold"><Zap className="w-3 h-3" /> MULTI-ASSET ROUTING ENABLED</span>
|
|
<span className="flex items-center gap-2"><Wifi className="w-3 h-3 text-nofx-success" /> LOW LATENCY LINK: 12ms</span>
|
|
|
|
{/* Dynamic Coins */}
|
|
{Object.entries(prices).map(([symbol, price]) => (
|
|
<span key={symbol} className="flex items-center gap-2">
|
|
{tickerLabels[symbol] || symbol.toUpperCase()} <span className="text-nofx-success">${price}</span>
|
|
</span>
|
|
))}
|
|
|
|
<span className="flex items-center gap-2"><CircuitBoard className="w-3 h-3 text-nofx-accent" /> AI MODEL: Claude Opus 4.6</span>
|
|
|
|
{/* Duplicate sequence for seamless loop effect (basic set) */}
|
|
{Object.entries(prices).map(([symbol, price]) => (
|
|
<span key={`${symbol} -dup`} className="flex items-center gap-2 md:hidden">
|
|
{tickerLabels[symbol] || symbol.toUpperCase()} <span className="text-nofx-success">${price}</span>
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
</section >
|
|
)
|
|
}
|
|
|
|
import { OFFICIAL_LINKS } from '../../../constants/branding'
|
|
|
|
function CommunityStats() {
|
|
const { stars, forks, contributors, isLoading, error } = useGitHubStats('NoFxAiOS', 'nofx')
|
|
|
|
const stats = [
|
|
{
|
|
label: 'GITHUB STARS',
|
|
value: isLoading ? '...' : (error ? '10,500+' : stars.toLocaleString()),
|
|
icon: Star,
|
|
color: 'text-nofx-gold',
|
|
href: OFFICIAL_LINKS.github
|
|
},
|
|
{
|
|
label: 'FORKS',
|
|
value: isLoading ? '...' : (error ? '2,800+' : forks.toLocaleString()),
|
|
icon: GitFork,
|
|
color: 'text-nofx-accent',
|
|
href: `${OFFICIAL_LINKS.github}/fork`
|
|
},
|
|
{
|
|
label: 'CONTRIBUTORS',
|
|
value: isLoading ? '...' : (contributors > 0 ? contributors : '50+'),
|
|
icon: Users,
|
|
color: 'text-nofx-success',
|
|
href: `${OFFICIAL_LINKS.github}/graphs/contributors`
|
|
},
|
|
{
|
|
label: 'DEV COMMUNITY',
|
|
value: '6,600+',
|
|
icon: MessageCircle,
|
|
color: 'text-nofx-accent',
|
|
href: OFFICIAL_LINKS.telegram
|
|
}
|
|
]
|
|
|
|
return (
|
|
<div className="mt-12 grid grid-cols-2 md:grid-cols-4 gap-4 w-full max-w-4xl">
|
|
{stats.map((stat, i) => (
|
|
<a
|
|
key={i}
|
|
href={stat.href}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex flex-col items-center justify-center p-3 rounded bg-nofx-bg-lighter border border-[rgba(26,24,19,0.14)] group hover:border-nofx-gold/30 transition-all cursor-pointer hover:bg-nofx-bg-deeper"
|
|
>
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<stat.icon className={`w-4 h-4 ${stat.color}`} />
|
|
<span className="text-[10px] font-mono text-nofx-text-muted tracking-wider">{stat.label}</span>
|
|
</div>
|
|
<span className="text-xl font-bold font-mono text-nofx-text group-hover:text-nofx-gold transition-colors">{stat.value}</span>
|
|
</a>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|