feat(web): add Agent Terminal panel to landing page

- Add AgentTerminal component with trading dashboard UI
- Display Portfolio PnL, metrics, order book, positions
- macOS-style terminal header with window controls
- Integrate into TerminalHero right column
- Remove unnecessary glow effects for cleaner look
This commit is contained in:
tinkle-community
2026-02-06 02:13:13 +08:00
parent 8896de2642
commit b70b047f75
4 changed files with 234 additions and 137 deletions
@@ -0,0 +1,195 @@
import { motion } from 'framer-motion'
export default function AgentTerminal() {
return (
<motion.div
initial={{ opacity: 0, y: 30, rotate: 0 }}
animate={{ opacity: 1, y: 0, rotate: 2 }}
transition={{ duration: 0.8, delay: 0.3 }}
className="w-[380px] lg:w-[440px] relative group"
>
{/* Terminal frame */}
<div className="relative bg-[#0B0F14] rounded-2xl overflow-hidden shadow-2xl shadow-black/80 border border-zinc-800/80">
{/* Scanline overlay */}
<div className="absolute inset-0 pointer-events-none z-50 opacity-[0.02]" style={{
backgroundImage: 'repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,255,255,0.03) 2px, rgba(255,255,255,0.03) 4px)'
}} />
{/* Header bar - macOS style */}
<div className="flex items-center justify-between px-4 py-2.5 bg-[#0D1117] border-b border-zinc-800/60">
{/* Window controls */}
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5">
<div className="w-3 h-3 rounded-full bg-[#ff5f57] hover:brightness-110 transition-all" />
<div className="w-3 h-3 rounded-full bg-[#febc2e] hover:brightness-110 transition-all" />
<div className="w-3 h-3 rounded-full bg-[#28c840] hover:brightness-110 transition-all" />
</div>
</div>
{/* Title */}
<div className="absolute left-1/2 -translate-x-1/2 flex items-center gap-2">
<span className="text-zinc-400 text-xs font-mono">NOFX Agent Terminal</span>
</div>
{/* Live indicator */}
<div className="flex items-center gap-1.5 px-2 py-0.5 rounded bg-green-500/10 border border-green-500/20">
<div className="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse" />
<span className="text-green-400 text-[10px] font-mono uppercase tracking-wider">Live</span>
</div>
</div>
{/* Portfolio PnL Section */}
<div className="p-4 border-b border-zinc-800/40">
<div className="flex items-center justify-between mb-3">
<span className="text-zinc-500 text-xs font-mono uppercase tracking-wider">Portfolio PnL</span>
<div className="flex gap-1">
<button className="px-2 py-0.5 bg-nofx-gold/20 border border-nofx-gold/30 rounded text-[10px] text-nofx-gold font-mono">24H</button>
<button className="px-2 py-0.5 text-[10px] text-zinc-600 font-mono hover:text-zinc-400 transition-colors">7D</button>
<button className="px-2 py-0.5 text-[10px] text-zinc-600 font-mono hover:text-zinc-400 transition-colors">30D</button>
</div>
</div>
<div className="flex items-baseline gap-3">
<span className="text-3xl font-bold text-green-400 font-mono tracking-tight">+$12,847.50</span>
<span className="text-green-500/80 text-sm font-mono">+8.42%</span>
</div>
{/* Chart Area */}
<div className="mt-4 h-16 rounded-lg overflow-hidden relative">
<svg className="w-full h-full" preserveAspectRatio="none" viewBox="0 0 400 64">
<defs>
<linearGradient id="chartGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stopColor="#22C55E" stopOpacity="0.2" />
<stop offset="100%" stopColor="#22C55E" stopOpacity="0" />
</linearGradient>
</defs>
<path
d="M0,56 C40,52 80,48 120,40 C160,32 200,28 240,24 C280,20 320,16 360,12 L400,8 L400,64 L0,64 Z"
fill="url(#chartGradient)"
/>
<path
d="M0,56 C40,52 80,48 120,40 C160,32 200,28 240,24 C280,20 320,16 360,12 L400,8"
fill="none"
stroke="#22C55E"
strokeWidth="1.5"
/>
</svg>
</div>
</div>
{/* Metrics Row */}
<div className="grid grid-cols-3 divide-x divide-zinc-800/40 border-b border-zinc-800/40">
<div className="p-3 text-center">
<div className="text-zinc-500 text-[10px] font-mono uppercase tracking-wider mb-1">OI</div>
<div className="text-white font-bold font-mono">$847M</div>
<div className="text-green-500 text-[10px] font-mono"> 2.1%</div>
</div>
<div className="p-3 text-center">
<div className="text-zinc-500 text-[10px] font-mono uppercase tracking-wider mb-1">Netflow</div>
<div className="text-green-400 font-bold font-mono">+$124M</div>
<div className="text-zinc-500 text-[10px] font-mono">24h inflow</div>
</div>
<div className="p-3 text-center">
<div className="text-zinc-500 text-[10px] font-mono uppercase tracking-wider mb-1">L/S Ratio</div>
<div className="text-white font-bold font-mono">1.24</div>
<div className="flex gap-0.5 mt-1 px-2">
<div className="h-1 bg-green-500/60 rounded-l flex-[55]" />
<div className="h-1 bg-red-500/60 rounded-r flex-[45]" />
</div>
</div>
</div>
{/* Order Book */}
<div className="p-4 border-b border-zinc-800/40">
<div className="flex items-center justify-between mb-3">
<span className="text-zinc-400 text-xs font-mono uppercase tracking-wider">Order Book</span>
<span className="text-zinc-600 text-[10px] font-mono">Spread: <span className="text-nofx-gold">0.02%</span></span>
</div>
<div className="grid grid-cols-2 gap-3">
{/* Asks */}
<div className="space-y-1">
{[
{ price: '97,289.50', amount: '2.451', depth: 70 },
{ price: '97,267.00', amount: '1.832', depth: 55 },
{ price: '97,251.00', amount: '0.945', depth: 30 },
].map((ask, i) => (
<div key={i} className="relative flex justify-between text-[11px] py-1 px-1.5 rounded">
<div className="absolute inset-0 bg-red-500/10 rounded-sm" style={{ width: `${ask.depth}%` }} />
<span className="relative text-red-400 font-mono">{ask.price}</span>
<span className="relative text-zinc-500 font-mono">{ask.amount}</span>
</div>
))}
</div>
{/* Bids */}
<div className="space-y-1">
{[
{ price: '97,244.50', amount: '3.127', depth: 85 },
{ price: '97,221.00', amount: '4.592', depth: 100 },
{ price: '97,198.00', amount: '1.845', depth: 50 },
].map((bid, i) => (
<div key={i} className="relative flex justify-between text-[11px] py-1 px-1.5 rounded">
<div className="absolute inset-0 bg-green-500/10 rounded-sm" style={{ width: `${bid.depth}%` }} />
<span className="relative text-green-400 font-mono">{bid.price}</span>
<span className="relative text-zinc-500 font-mono">{bid.amount}</span>
</div>
))}
</div>
</div>
</div>
{/* Active Positions */}
<div className="p-4">
<div className="flex items-center justify-between mb-3">
<span className="text-zinc-400 text-xs font-mono uppercase tracking-wider">Positions</span>
<span className="text-green-400 text-xs font-mono font-medium">+$12,847</span>
</div>
<div className="space-y-2">
{[
{ coin: 'BTC', name: 'BTC-PERP', size: '0.5', profit: '+$6,420', percent: '+12.8%', color: '#F7931A' },
{ coin: 'ETH', name: 'ETH-PERP', size: '3.2', profit: '+$4,127', percent: '+7.6%', color: '#627EEA' },
{ coin: 'BNB', name: 'BNB-PERP', size: '8.5', profit: '+$2,300', percent: '+5.2%', color: '#F3BA2F' },
].map((pos, i) => (
<div key={i} className="flex items-center justify-between py-2 px-2 rounded-lg bg-zinc-900/50 hover:bg-zinc-800/50 transition-colors">
<div className="flex items-center gap-3">
<div
className="w-8 h-8 rounded-lg flex items-center justify-center text-xs font-bold border"
style={{
backgroundColor: pos.color + '15',
borderColor: pos.color + '30',
color: pos.color
}}
>
{pos.coin}
</div>
<div>
<div className="text-white text-sm font-mono">{pos.name}</div>
<div className="flex items-center gap-2 text-[10px]">
<span className="text-green-400 bg-green-500/10 px-1.5 py-0.5 rounded font-mono">LONG</span>
<span className="text-zinc-500 font-mono">{pos.size} {pos.coin}</span>
</div>
</div>
</div>
<div className="text-right">
<div className="text-green-400 font-mono font-medium">{pos.profit}</div>
<div className="text-green-500/70 text-[10px] font-mono">{pos.percent}</div>
</div>
</div>
))}
</div>
</div>
{/* Footer status bar */}
<div className="px-4 py-2 bg-[#0D1117] border-t border-zinc-800/60 flex items-center justify-between">
<div className="flex items-center gap-3 text-[10px] font-mono text-zinc-600">
<span className="flex items-center gap-1">
<div className="w-1.5 h-1.5 bg-green-500 rounded-full" />
Connected
</span>
<span>Latency: 12ms</span>
</div>
<div className="text-[10px] font-mono text-zinc-600">
mainnet v2.4.0
</div>
</div>
</div>
</motion.div>
)
}
+15 -21
View File
@@ -2,6 +2,7 @@ import { motion } from 'framer-motion'
import { ArrowRight, Github } from 'lucide-react'
import { Marquee } from './Marquee'
import { OFFICIAL_LINKS } from '../../../constants/branding'
import AgentTerminal from './AgentTerminal'
export default function BrandHero() {
const handleScroll = () => {
@@ -75,32 +76,25 @@ export default function BrandHero() {
</motion.div>
</div>
{/* Right Visual - Mascot */}
<div className="flex-1 relative flex items-end justify-center lg:justify-end overflow-hidden">
{/* Abstract background elements */}
<div className="absolute top-1/4 right-0 w-[600px] h-[600px] bg-nofx-accent/20 rounded-full blur-[100px] pointer-events-none" />
<div className="absolute bottom-0 left-10 w-[400px] h-[400px] bg-nofx-gold/10 rounded-full blur-[80px] pointer-events-none" />
{/* Right Visual - Agent Terminal */}
<div className="flex-1 relative overflow-visible flex items-center justify-center py-8 lg:py-0 min-h-[600px]">
{/* Background gradient orbs */}
<div className="absolute top-1/2 right-[15%] -translate-y-1/2 w-[450px] h-[450px] rounded-full bg-gradient-to-br from-nofx-gold/20 via-nofx-gold/5 to-transparent blur-[80px]" />
<div className="absolute top-[25%] right-[35%] w-[250px] h-[250px] rounded-full bg-nofx-accent/10 blur-[60px]" />
{/* Grid Pattern */}
<div className="absolute inset-0 opacity-20"
{/* Subtle dot grid */}
<div
className="absolute inset-0 opacity-[0.04]"
style={{
backgroundImage: 'linear-gradient(#333 1px, transparent 1px), linear-gradient(90deg, #333 1px, transparent 1px)',
backgroundSize: '40px 40px'
backgroundImage: 'radial-gradient(circle at 1px 1px, rgba(255,255,255,0.4) 1px, transparent 0)',
backgroundSize: '32px 32px'
}}
/>
<motion.div
initial={{ opacity: 0, y: 100 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.2 }}
className="relative z-10 w-full h-full flex items-end justify-center lg:justify-end lg:pr-10"
>
<img
src="/images/nofx_mascot.png"
alt="Cyberpunk Mascot"
className="h-[80vh] object-contain drop-shadow-[0_0_50px_rgba(0,0,0,0.5)]"
/>
</motion.div>
{/* Terminal Panel */}
<div className="relative z-10">
<AgentTerminal />
</div>
</div>
</div>
</section>
@@ -1,7 +1,8 @@
import { motion } from 'framer-motion'
import { ArrowRight, Shield, Activity, CircuitBoard, Cpu, Wifi, Globe, Lock, Zap, Star, GitFork, Users, MessageCircle } from 'lucide-react'
import { ArrowRight, Shield, Activity, CircuitBoard, Wifi, Globe, Zap, Star, GitFork, Users, MessageCircle } from 'lucide-react'
import { useState, useEffect } from 'react'
import { useGitHubStats } from '../../../hooks/useGitHubStats'
import AgentTerminal from '../brand/AgentTerminal'
export default function TerminalHero() {
@@ -88,10 +89,10 @@ export default function TerminalHero() {
{/* 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 - Moved to Left to avoid covering face */}
<div className="md:hidden absolute top-24 left-4 z-0 opacity-40 pointer-events-none">
<div className="w-24 h-24 border border-dashed border-nofx-gold/30 rounded-full animate-spin-slow flex items-center justify-center">
<div className="w-16 h-16 border border-nofx-accent/30 rounded-full"></div>
{/* 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>
@@ -236,72 +237,25 @@ export default function TerminalHero() {
</div>
</div>
{/* RIGHT COLUMN: HOLOGRAPHIC DISPLAY - Absolute Overlay for "Far Right" Effect on Desktop, Background on Mobile */}
<div className="absolute top-20 md:top-0 right-0 h-[50vh] md:h-full w-full lg:w-[45vw] flex pointer-events-none items-center justify-center z-0 opacity-80 lg:opacity-100 mix-blend-normal">
<div className="relative w-full h-full flex items-center justify-center perspective-1000">
{/* 3D Hologram Effect Container */}
<div className="relative w-full h-[90%] flex items-center justify-center transform-style-3d rotate-y-[-12deg]">
{/* RIGHT COLUMN: Agent 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-gradient-to-br from-nofx-gold/10 via-nofx-gold/5 to-transparent blur-[100px] pointer-events-none"></div>
{/* Scanning Grid behind Mascot - Mobile Optimized */}
<div className="absolute inset-x-0 top-[10%] bottom-[10%] bg-[linear-gradient(rgba(0,240,255,0.05)_1px,transparent_1px),linear-gradient(90deg,rgba(0,240,255,0.05)_1px,transparent_1px)] bg-[size:20px_20px] [mask-image:radial-gradient(ellipse_at_center,black_40%,transparent_80%)] mobile-grid-pulse"></div>
{/* The Mascot Image with Glitch/Holo Effects */}
<div className="relative z-10 w-full h-full opacity-100 transition-all duration-500 group flex flex-col justify-end pointer-events-auto">
<div className="absolute inset-x-0 bottom-0 top-1/2 bg-nofx-accent/5 blur-[60px] rounded-full animate-pulse-slow transition-colors duration-500 group-hover:bg-nofx-gold/20"></div>
{/* Mobile Holo-Portrait Style - Full Color & Optimized & Premium Desktop */}
<div className="relative w-full h-full flex items-end justify-center">
<img
src="/images/nofx_mascot.png"
alt="Agent NoFX"
className="w-full h-full object-contain object-bottom char-premium-effects animate-breath-mobile transition-all duration-500"
style={{
maskImage: 'radial-gradient(ellipse at center, black 60%, transparent 100%), linear-gradient(to bottom, black 0%, black 85%, transparent 100%)',
WebkitMaskImage: 'radial-gradient(ellipse at center, black 60%, transparent 100%), linear-gradient(to bottom, black 0%, black 85%, transparent 100%)',
maskComposite: 'intersect',
WebkitMaskComposite: 'source-in'
}}
/>
{/* Dynamic Holographic Overlay - Premium Noise & Gradient */}
<div className="absolute inset-0 w-full h-full holo-overlay animate-holo opacity-80 pointer-events-none"
style={{
maskImage: 'url(/images/nofx_mascot.png)',
WebkitMaskImage: 'url(/images/nofx_mascot.png)',
maskSize: 'contain',
WebkitMaskSize: 'contain',
maskPosition: 'bottom center',
WebkitMaskPosition: 'bottom center',
maskRepeat: 'no-repeat',
WebkitMaskRepeat: 'no-repeat'
}}
/>
</div>
{/* Holo Scan Line - Subtle on Mobile */}
<div className="absolute w-full h-1 bg-nofx-accent/30 drop-shadow-[0_0_10px_rgba(0,240,255,0.8)] top-0 animate-scan-fast pointer-events-none mix-blend-overlay"></div>
{/* Mobile Glitch Overlay - Reduced Intensity */}
<div className="absolute inset-0 bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-10 mix-blend-overlay md:hidden animate-pulse-fast"></div>
</div>
</div>
{/* Floating Data Widgets around Hologram */}
<motion.div
animate={{ y: [0, -10, 0] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
className="absolute top-[30%] left-[10%] bg-black/80 border border-nofx-accent/30 p-2 rounded backdrop-blur-md shadow-neon-blue hidden md:block"
>
<Cpu className="w-5 h-5 text-nofx-accent" />
</motion.div>
<motion.div
animate={{ y: [0, 10, 0] }}
transition={{ duration: 5, repeat: Infinity, ease: "easeInOut", delay: 1 }}
className="absolute bottom-[20%] right-[20%] bg-black/80 border border-nofx-gold/30 p-2 rounded backdrop-blur-md shadow-neon hidden md:block"
>
<Lock className="w-5 h-5 text-nofx-gold" />
</motion.div>
{/* Subtle grid fade */}
<div
className="absolute inset-0 opacity-[0.03] pointer-events-none"
style={{
backgroundImage: 'radial-gradient(circle at 1px 1px, rgba(255,255,255,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>
{/* Agent Terminal Panel */}
<div className="relative z-20 pointer-events-auto">
<AgentTerminal />
</div>
</div>
+1 -47
View File
@@ -938,35 +938,7 @@ tr:hover {
animation: holo-shift 8s ease infinite, holo-flicker 4s infinite;
}
/* Mobile Breathing Animation */
@keyframes holo-breath {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
.animate-breath-mobile {
animation: none;
}
@media (max-width: 768px) {
.animate-breath-mobile {
animation: holo-breath 4s ease-in-out infinite;
}
/* Optimize premium effects for mobile */
.char-premium-effects {
filter:
drop-shadow(0 0 1px rgba(240, 185, 11, 0.6)) drop-shadow(0 0 1px rgba(0, 240, 255, 0.5));
}
}
/* Holographic overlay effect */
.holo-overlay {
/* Complex gradient + noise for texture */
background:
@@ -978,22 +950,4 @@ tr:hover {
rgba(240, 185, 11, 0.1) 360deg);
mix-blend-mode: overlay;
background-blend-mode: overlay, normal;
}
/* Chromatic Aberration & Rim Light */
.char-premium-effects {
filter:
/* Rim Light: Main Warm Gold + Sharp White Highlight (No Cyan to avoid green) */
drop-shadow(0 0 2px rgba(240, 185, 11, 0.6)) drop-shadow(0 0 1px rgba(255, 255, 255, 0.4))
/* Chromatic Aberration: Red/Blue shift (Subtle) */
drop-shadow(-1px 0 0 rgba(255, 50, 50, 0.4)) drop-shadow(1px 0 0 rgba(50, 50, 255, 0.4));
transition: filter 0.3s ease;
}
.char-premium-effects:hover {
filter:
/* Intense Gold Glow on Hover */
drop-shadow(0 0 8px rgba(240, 185, 11, 0.8)) drop-shadow(0 0 2px rgba(255, 255, 255, 0.6))
/* Enhanced Aberration */
drop-shadow(-2px 0 0 rgba(255, 50, 50, 0.5)) drop-shadow(2px 0 0 rgba(50, 50, 255, 0.5));
}