Compare commits

...

2 Commits

Author SHA1 Message Date
Flatlogic Bot
9df3e44fd0 V0.1 2026-03-20 06:34:08 +00:00
Flatlogic Bot
2daa593025 Autosave: 20260320-055648 2026-03-20 05:56:48 +00:00
28 changed files with 2230 additions and 222 deletions

View File

@ -9,7 +9,7 @@ Telegram C2 command, and a cyber-terminal dashboard.
``` ```
worker.py worker.py
├── Thread A — FastAPI + Dashboard (port 7000) ├── Thread A — FastAPI + Dashboard (port 3001)
├── Thread B — Async Playwright Scraper (nuclear_engine) ├── Thread B — Async Playwright Scraper (nuclear_engine)
└── Thread C — Telegram C2 Command Listener └── Thread C — Telegram C2 Command Listener
``` ```
@ -45,7 +45,7 @@ playwright install chromium
python worker.py python worker.py
``` ```
Then open: **http://localhost:7000** Then open: **http://localhost:3001**
--- ---

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

9
branding_decision.md Normal file
View File

@ -0,0 +1,9 @@
# Branding Decision: BidWraith
**Name:** BidWraith (Combined)
**Rationale:** The combined form implies a singular, stealthy, powerful entity (a "wraith" of the bidding world). It fits the "cyberpunk/tech" aesthetic better than the separated version.
**Visual Aesthetic:**
- **Palette:** Deep void blacks (#050505), toxic neons (acid green #39FF14, cyber blue #00F3FF, or hazard yellow #FFD700).
- **Typography:** Monospaced fonts for technical feel, combined with bold, futuristic headers.
- **Vibe:** "Tech-noir", interactive data streams, floating UI elements, and high-fidelity animations.

View File

@ -10,6 +10,7 @@
- Direct, minimal punctuation. Prefers intent understood from brief messages; implement changes efficiently without over-explaining. - Direct, minimal punctuation. Prefers intent understood from brief messages; implement changes efficiently without over-explaining.
- When stuck (e.g. CLI, setup), appreciates **step-by-step** instructions, one by one. - When stuck (e.g. CLI, setup), appreciates **step-by-step** instructions, one by one.
- Cares about “best ultimate outcome”; open to bold changes when given full permission. - Cares about “best ultimate outcome”; open to bold changes when given full permission.
- Demands excellence in UX/UI: expects modern, premium, high-quality, and magical interfaces that compete with global top-tier SaaS. Refuses "toy" designs or fake functions.
--- ---
@ -28,5 +29,4 @@
--- ---
*The AI adds to this file when it learns or discovers something about Abbas (general, not project-specific).* *The AI adds to this file when it learns or discovers something about Abbas (general, not project-specific).*

View File

@ -17,8 +17,4 @@
- Chose default Node installer path over portable; docs updated to match. - Chose default Node installer path over portable; docs updated to match.
- Asked for MD file discipline (when to update what); FEEDBACK.md and ERROR.md added on request. - Asked for MD file discipline (when to update what); FEEDBACK.md and ERROR.md added on request.
- **Frontend:** Strongly disliked the Session 22 redesign (sidebar nav, grid/glow background, new fonts, heavy panels). Prefers the original React UI and even the legacy HTML dashboard over that. Reverted in Session 24. Future UI changes should be small and match his taste — ask or reference what he likes before a big visual overhaul. - **Frontend:** Strongly disliked the Session 22 redesign (sidebar nav, grid/glow background, new fonts, heavy panels). Prefers the original React UI and even the legacy HTML dashboard over that. Reverted in Session 24. Future UI changes should be small and match his taste — ask or reference what he likes before a big visual overhaul.
- **Design Preference:** Demands professional, high-end UX/UI that can compete with top-tier international SaaS (Google-level design, magical 3D/animations, premium feel). Explicit permission granted to use any tech (Next.js, React, Tailwind, Framer Motion, Three.js, GSAP, etc.) to achieve this. Requires light/dark mode support with vivid, joyful, magical themes (e.g., bright lavender/ivory palette for light mode). Rejection of "toy-like" designs.
---
*Add new bullets above when something is learned that future sessions should use.*

View File

@ -0,0 +1,95 @@
"use client";
import { motion } from 'framer-motion';
import { TerminalSquare, Filter, RefreshCw, Cpu, Brain, Zap } from 'lucide-react';
const mockLogs = [
{ id: 1, time: '10:41:30', agent: 'Analyst', type: 'REJECT', cost: '$0.0012', prompt: 'Evaluate lot: "RTX 4090 Box Only" against criteria...', response: '{"verdict": "REJECT", "reason": "Listing explicitly states \'Box Only\', violating target criteria."}' },
{ id: 2, time: '10:40:12', agent: 'Analyst', type: 'APPROVE', cost: '$0.0025', prompt: 'Evaluate lot: "ThinkPad T14 Gen 3" against criteria...', response: '{"verdict": "APPROVE", "reason": "Matches specs, good condition, price $450 is significantly below $800 avg."}' },
{ id: 3, time: '10:39:45', agent: 'Strategist', type: 'OPTIMIZE', cost: '$0.0050', prompt: 'Analyze last 24h win rates on ShopGoodwill...', response: '{"action": "UPDATE_SCHEDULE", "target": "ShopGoodwill", "new_cron": "0 * * * *"}' },
{ id: 4, time: '10:35:10', agent: 'Scout', type: 'INFO', cost: '$0.0005', prompt: 'Parse unstructured layout changes on eBay UK...', response: '{"action": "SELECTORS_UPDATED", "confidence": 0.98 }' },
];
export default function AILogPage() {
return (
<div className="container mx-auto px-6 py-12 h-[calc(100vh-80px)] flex flex-col">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 shrink-0 gap-4">
<div>
<h1 className="text-4xl font-extrabold tracking-tight mb-2 flex items-center gap-3">
Neural Telemetry <span className="text-primary"><Brain className="w-8 h-8" /></span>
</h1>
<p className="text-muted-foreground text-lg">Raw input/output logs from all connected LLM agents.</p>
</div>
<div className="flex gap-3">
<button className="flex items-center gap-2 px-5 py-2.5 bg-secondary hover:bg-secondary/80 border-2 border-border/50 rounded-full font-bold transition-all hover:scale-105 shadow-sm">
<Filter className="w-4 h-4" /> Filter
</button>
<button className="flex items-center gap-2 px-5 py-2.5 bg-primary hover:bg-primary/90 text-primary-foreground rounded-full font-bold transition-all hover:scale-105 shadow-lg hover:shadow-primary/30">
<RefreshCw className="w-4 h-4" /> Refresh
</button>
</div>
</div>
<div className="flex-1 bg-card border-2 border-border/50 rounded-3xl overflow-hidden flex flex-col shadow-sm">
<div className="grid grid-cols-12 gap-4 p-5 border-b-2 border-border/50 bg-secondary/30 text-sm font-bold text-muted-foreground uppercase tracking-wider shrink-0">
<div className="col-span-2 md:col-span-2">Time / Agent</div>
<div className="col-span-2 md:col-span-1">Action</div>
<div className="col-span-4 md:col-span-4">Prompt Context</div>
<div className="col-span-4 md:col-span-4">JSON Response</div>
<div className="hidden md:flex col-span-1 justify-end">Est. Cost</div>
</div>
<div className="flex-1 overflow-auto p-4 space-y-3 custom-scrollbar">
{mockLogs.map((log, i) => (
<motion.div
key={log.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: i * 0.1, type: "spring" }}
className="grid grid-cols-12 gap-4 p-4 bg-secondary/20 hover:bg-secondary/50 border border-border/30 rounded-2xl transition-colors cursor-pointer group"
>
<div className="col-span-2 md:col-span-2 flex flex-col justify-center">
<span className="font-bold text-foreground">{log.time}</span>
<span className="text-xs font-semibold text-muted-foreground flex items-center gap-1 mt-1">
{log.agent === 'Analyst' ? <Brain className="w-3 h-3 text-purple-500" /> : log.agent === 'Strategist' ? <Zap className="w-3 h-3 text-amber-500" /> : <Cpu className="w-3 h-3 text-blue-500" />}
{log.agent}
</span>
</div>
<div className="col-span-2 md:col-span-1 flex items-center">
<span className={`px-3 py-1 rounded-full text-xs font-bold ${
log.type === 'APPROVE' ? 'bg-green-500/10 text-green-600 dark:text-green-400' :
log.type === 'REJECT' ? 'bg-red-500/10 text-red-600 dark:text-red-400' :
log.type === 'OPTIMIZE' ? 'bg-amber-500/10 text-amber-600 dark:text-amber-400' :
'bg-blue-500/10 text-blue-600 dark:text-blue-400'
}`}>
{log.type}
</span>
</div>
<div className="col-span-4 md:col-span-4 text-muted-foreground text-sm flex items-center truncate font-medium pr-4">
{log.prompt}
</div>
<div className="col-span-4 md:col-span-4 text-sm flex items-center truncate font-mono text-foreground/80 bg-background/50 px-3 py-1.5 rounded-lg border border-border/30">
{log.response}
</div>
<div className="hidden md:flex col-span-1 items-center justify-end text-sm font-bold text-muted-foreground group-hover:text-primary transition-colors">
{log.cost}
</div>
</motion.div>
))}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
className="text-center p-12 text-muted-foreground flex flex-col items-center justify-center gap-4"
>
<div className="p-4 bg-secondary/30 rounded-full animate-pulse">
<TerminalSquare className="w-8 h-8 text-muted-foreground/50" />
</div>
<span className="font-medium text-lg">End of stream. Awaiting new neural inferences...</span>
</motion.div>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,173 @@
"use client";
import { motion } from 'framer-motion';
import { Activity, Zap, ShieldAlert, Cpu, TrendingUp, CheckCircle2, Clock } from 'lucide-react';
const stats = [
{ title: 'Lots Scanned', value: '142,893', icon: Activity, color: 'text-blue-500', bg: 'bg-blue-500/10', border: 'border-blue-500/20' },
{ title: 'Active Targets', value: '84', icon: Zap, color: 'text-amber-500', bg: 'bg-amber-500/10', border: 'border-amber-500/20' },
{ title: 'Alerts Fired', value: '1,204', icon: TrendingUp, color: 'text-purple-500', bg: 'bg-purple-500/10', border: 'border-purple-500/20' },
{ title: 'AI Decisions', value: '89,430', icon: Cpu, color: 'text-green-500', bg: 'bg-green-500/10', border: 'border-green-500/20' },
];
const containerVariants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 20, scale: 0.9 },
show: { opacity: 1, y: 0, scale: 1, transition: { type: "spring" as const, stiffness: 300, damping: 24 } }
};
export default function DashboardPage() {
return (
<div className="container mx-auto px-6 py-12 relative">
<div className="absolute top-0 right-0 w-[50%] h-[50%] bg-primary/5 rounded-full blur-[150px] pointer-events-none -z-10" />
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
className="flex flex-col md:flex-row justify-between items-start md:items-end mb-12 gap-4"
>
<div>
<h1 className="text-5xl font-black tracking-tight mb-3 flex items-center gap-4 drop-shadow-sm">
System Overview <motion.span animate={{ y: [0, -10, 0] }} transition={{ duration: 2, repeat: Infinity }} className="text-4xl">🚀</motion.span>
</h1>
<p className="text-muted-foreground text-xl font-medium">Real-time status of all your autonomous AI agents.</p>
</div>
<motion.div
whileHover={{ scale: 1.05 }}
className="flex items-center gap-3 px-6 py-3 bg-green-500/10 text-green-500 border border-green-500/20 rounded-full font-black shadow-inner shadow-green-500/10 backdrop-blur-md"
>
<span className="relative flex h-3.5 w-3.5">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex rounded-full h-3.5 w-3.5 bg-green-500 shadow-[0_0_10px_rgba(34,197,94,1)]"></span>
</span>
All Systems Go
</motion.div>
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
animate="show"
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-12"
>
{stats.map((stat, i) => (
<motion.div
key={i}
variants={itemVariants}
whileHover={{ y: -5, scale: 1.02 }}
className={`p-6 bg-card/80 backdrop-blur-xl border border-white/5 hover:${stat.border} rounded-[2rem] shadow-lg hover:shadow-xl transition-all relative overflow-hidden group`}
>
<div className={`absolute -right-10 -top-10 w-32 h-32 ${stat.bg} rounded-full blur-[40px] group-hover:scale-150 transition-transform duration-700`} />
<div className="flex justify-between items-start mb-6 relative z-10">
<div className="text-muted-foreground font-bold text-lg">{stat.title}</div>
<div className={`p-3 rounded-2xl ${stat.bg} shadow-inner`}>
<stat.icon className={`w-6 h-6 ${stat.color}`} />
</div>
</div>
<div className="text-5xl font-black text-foreground drop-shadow-md relative z-10">{stat.value}</div>
</motion.div>
))}
</motion.div>
<div className="grid lg:grid-cols-3 gap-8">
<div className="col-span-2">
<motion.div
initial={{ opacity: 0, x: -30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.3, type: "spring" as const }}
className="p-8 bg-card/80 backdrop-blur-xl border border-white/5 rounded-[2.5rem] h-[500px] flex flex-col shadow-xl relative overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 to-transparent pointer-events-none" />
<div className="flex items-center gap-4 mb-8 pb-6 border-b border-border/50 relative z-10">
<Clock className="w-8 h-8 text-primary" />
<h2 className="text-3xl font-black tracking-tight">Live Activity Log</h2>
</div>
<div className="flex-1 overflow-auto space-y-4 pr-4 custom-scrollbar relative z-10">
{[
{ time: '10:42:01 AM', msg: 'Scout Agent detected new items on eBay_UK...', type: 'normal' },
{ time: '10:41:55 AM', msg: 'Analyst Agent processing 14 lots for "RTX 4090"...', type: 'normal' },
{ time: '10:41:30 AM', msg: 'MATCH FOUND: "RTX 4090 Box Only" - Rejected by AI (Reason: Scam)', type: 'warning' },
{ time: '10:40:12 AM', msg: 'HIGH VALUE MATCH: "ThinkPad T14 Gen 3" - $450 (Est: $800). Alert Sent.', type: 'success' },
{ time: '10:39:45 AM', msg: 'Strategist Agent optimizing scrape timings for ShopGoodwill...', type: 'normal' },
].map((log, j) => (
<motion.div
key={j}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.5 + (j * 0.1) }}
className={`flex gap-4 p-5 rounded-2xl ${
log.type === 'warning' ? 'bg-amber-500/10 border border-amber-500/20' :
log.type === 'success' ? 'bg-green-500/10 border border-green-500/20 shadow-[0_0_15px_rgba(34,197,94,0.15)]' :
'bg-secondary/40 border border-white/5'
} backdrop-blur-sm transition-all hover:scale-[1.01]`}
>
<span className={`whitespace-nowrap font-bold ${
log.type === 'warning' ? 'text-amber-500' :
log.type === 'success' ? 'text-green-500' :
'text-muted-foreground'
}`}>{log.time}</span>
<span className={`font-bold ${
log.type === 'warning' ? 'text-amber-600 dark:text-amber-400' :
log.type === 'success' ? 'text-green-600 dark:text-green-400 flex items-center gap-2' :
'text-foreground'
}`}>
{log.type === 'success' && <CheckCircle2 className="w-5 h-5" />}
{log.msg}
</span>
</motion.div>
))}
</div>
</motion.div>
</div>
<div>
<motion.div
initial={{ opacity: 0, x: 30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.4, type: "spring" as const }}
className="p-8 bg-card/80 backdrop-blur-xl border border-white/5 rounded-[2.5rem] h-[500px] shadow-xl relative overflow-hidden"
>
<div className="absolute inset-0 bg-gradient-to-bl from-accent/5 to-transparent pointer-events-none" />
<div className="flex items-center gap-4 mb-8 pb-6 border-b border-border/50 relative z-10">
<Activity className="w-8 h-8 text-primary" />
<h2 className="text-3xl font-black tracking-tight">Node Health</h2>
</div>
<div className="space-y-8 relative z-10">
{[
{ name: 'eBay US', health: 100, color: 'bg-green-500', shadow: 'shadow-[0_0_15px_rgba(34,197,94,0.5)]' },
{ name: 'eBay UK', health: 98, color: 'bg-blue-500', shadow: 'shadow-[0_0_15px_rgba(59,130,246,0.5)]' },
{ name: 'HiBid', health: 85, color: 'bg-amber-500', shadow: 'shadow-[0_0_15px_rgba(245,158,11,0.5)]' },
{ name: 'ShopGoodwill', health: 99, color: 'bg-purple-500', shadow: 'shadow-[0_0_15px_rgba(168,85,247,0.5)]' }
].map((node, i) => (
<div key={i} className="group">
<div className="flex justify-between text-lg font-black mb-3">
<span>{node.name}</span>
<span className={node.health < 90 ? "text-amber-500" : "text-green-500"}>{node.health}%</span>
</div>
<div className="w-full bg-secondary/50 rounded-full h-4 overflow-hidden border border-white/5">
<motion.div
initial={{ width: 0 }}
animate={{ width: `${node.health}%` }}
transition={{ duration: 1.5, delay: 0.6 + (i * 0.15), type: "spring" as const, bounce: 0.2 }}
className={`${node.color} h-full rounded-full ${node.shadow} relative`}
>
<div className="absolute top-0 right-0 bottom-0 w-20 bg-gradient-to-r from-transparent to-white/30" />
</motion.div>
</div>
</div>
))}
</div>
</motion.div>
</div>
</div>
</div>
);
}

113
frontend/app/globals.css Normal file
View File

@ -0,0 +1,113 @@
@import "tailwindcss";
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--radius: 0.75rem;
}
@layer base {
:root {
--background: oklch(0.985 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(0.985 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(0.985 0 0);
--popover-foreground: oklch(0.145 0 0);
/* Vibrant tech blue/purple for fun tech style */
--primary: oklch(0.55 0.25 260);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.95 0.05 250);
--secondary-foreground: oklch(0.2 0.1 260);
--muted: oklch(0.96 0 0);
--muted-foreground: oklch(0.5 0 0);
/* Fun energetic orange accent */
--accent: oklch(0.7 0.2 40);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.6 0.2 20);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.9 0 0);
--input: oklch(0.9 0 0);
--ring: oklch(0.55 0.25 260);
}
.dark {
--background: oklch(0.15 0.02 260);
--foreground: oklch(0.985 0 0);
--card: oklch(0.18 0.03 260);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.15 0.02 260);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.65 0.25 260); /* Brighter in dark mode */
--primary-foreground: oklch(0.1 0.1 260);
--secondary: oklch(0.25 0.05 260);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.25 0.05 260);
--muted-foreground: oklch(0.7 0 0);
--accent: oklch(0.75 0.2 40);
--accent-foreground: oklch(0.1 0.1 40);
--destructive: oklch(0.5 0.2 20);
--destructive-foreground: oklch(0.985 0 0);
--border: oklch(0.25 0.05 260);
--input: oklch(0.25 0.05 260);
--ring: oklch(0.65 0.25 260);
}
body {
background-color: var(--background);
color: var(--foreground);
border-color: var(--border);
transition: background-color 0.3s ease, color 0.3s ease;
}
}
/* Base Fun Tech Styles */
.bg-gradient-tech {
background-image: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
}
.text-gradient-tech {
background-image: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Animated Grid Background */
.tech-grid {
background-size: 40px 40px;
background-image:
linear-gradient(to right, oklch(0.5 0 0 / 0.05) 1px, transparent 1px),
linear-gradient(to bottom, oklch(0.5 0 0 / 0.05) 1px, transparent 1px);
mask-image: radial-gradient(circle at center, black 40%, transparent 100%);
-webkit-mask-image: radial-gradient(circle at center, black 40%, transparent 100%);
}

View File

@ -0,0 +1,98 @@
"use client";
import { motion } from 'framer-motion';
import { Plus, Search, GripVertical, Settings2, Trash2, Target } from 'lucide-react';
const mockTargets = [
{ id: 1, term: 'ThinkPad T14 Gen 3', maxPrice: 600, weight: 1.5, status: 'active' },
{ id: 2, term: 'RTX 3080', maxPrice: 350, weight: 2.0, status: 'active' },
{ id: 3, term: 'iPhone 12 Pro Unlocked', maxPrice: 200, weight: 1.0, status: 'paused' },
];
export default function KeywordsPage() {
return (
<div className="container mx-auto px-6 py-12">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 gap-4">
<div>
<h1 className="text-4xl font-extrabold tracking-tight mb-2 flex items-center gap-3">
Target Vectors <span className="text-primary"><Target className="w-8 h-8" /></span>
</h1>
<p className="text-muted-foreground text-lg">Define the precise assets your AI agents should hunt for.</p>
</div>
<button className="flex items-center gap-3 px-6 py-3 bg-primary hover:bg-primary/90 text-primary-foreground rounded-full font-bold shadow-lg hover:shadow-primary/30 transition-all hover:scale-105 active:scale-95">
<Plus className="w-5 h-5" /> New Target
</button>
</div>
<div className="bg-card border-2 border-border/50 rounded-3xl overflow-hidden shadow-sm">
<div className="grid grid-cols-12 gap-4 p-6 border-b-2 border-border/50 bg-secondary/30 text-sm font-bold text-muted-foreground uppercase tracking-wider">
<div className="col-span-1">Sort</div>
<div className="col-span-5">Search Query</div>
<div className="col-span-2">Max Price</div>
<div className="col-span-2">Priority</div>
<div className="col-span-2 text-right">Actions</div>
</div>
<div className="divide-y divide-border/30">
{mockTargets.map((target, i) => (
<motion.div
key={target.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: i * 0.1, type: "spring" }}
className={`grid grid-cols-12 gap-4 p-6 items-center hover:bg-secondary/10 transition-colors group ${target.status === 'paused' ? 'opacity-50' : ''}`}
>
<div className="col-span-1 text-muted-foreground/50 group-hover:text-primary transition-colors">
<GripVertical className="w-6 h-6 cursor-grab" />
</div>
<div className="col-span-5 text-lg font-bold flex items-center gap-3">
<div className={`p-2 rounded-xl ${target.status === 'active' ? 'bg-primary/10 text-primary' : 'bg-muted text-muted-foreground'}`}>
<Search className="w-5 h-5" />
</div>
{target.term}
</div>
<div className="col-span-2 text-xl font-black text-foreground">
${target.maxPrice}
</div>
<div className="col-span-2">
<div className="flex items-center gap-3">
<div className="w-24 h-2.5 bg-secondary rounded-full overflow-hidden">
<div className="h-full bg-accent rounded-full transition-all" style={{width: `${(target.weight / 2) * 100}%`}}></div>
</div>
<span className="font-bold text-sm bg-accent/10 text-accent px-2 py-1 rounded-md">{target.weight}x</span>
</div>
</div>
<div className="col-span-2 flex justify-end gap-3 opacity-0 group-hover:opacity-100 transition-opacity">
<button className="p-2.5 bg-secondary hover:bg-primary/10 text-muted-foreground hover:text-primary rounded-xl transition-all shadow-sm hover:shadow-primary/20">
<Settings2 className="w-5 h-5" />
</button>
<button className="p-2.5 bg-secondary hover:bg-destructive/10 text-muted-foreground hover:text-destructive rounded-xl transition-all shadow-sm hover:shadow-destructive/20">
<Trash2 className="w-5 h-5" />
</button>
</div>
</motion.div>
))}
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
className="mt-10 p-8 border-2 border-primary/20 bg-primary/5 rounded-3xl"
>
<div className="flex items-center gap-3 mb-4">
<div className="p-3 bg-primary/20 rounded-2xl">
<Settings2 className="w-6 h-6 text-primary" />
</div>
<h3 className="text-2xl font-bold">Global AI Filtering Template</h3>
</div>
<p className="text-lg text-muted-foreground mb-6 font-medium">Define a natural language description for the LLM to verify against globally. For example: <span className="text-foreground bg-secondary px-2 py-0.5 rounded-md">"Item must not be broken. Must include power cable. Ignore 'box only' listings."</span></p>
<textarea
className="w-full h-32 bg-background/50 border-2 border-border/50 focus:border-primary rounded-2xl p-5 text-lg text-foreground font-medium outline-none resize-none transition-all shadow-inner placeholder:text-muted-foreground/50"
placeholder="Enter custom instructions for the Analyst Agent here..."
/>
</motion.div>
</div>
);
}

42
frontend/app/layout.tsx Normal file
View File

@ -0,0 +1,42 @@
import type { Metadata } from "next";
import { Inter, JetBrains_Mono } from "next/font/google";
import "./globals.css";
import Navbar from "@/components/Navbar";
import { ThemeProvider } from "@/components/theme-provider";
import PageTransition from "@/components/PageTransition";
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
const jetbrainsMono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-mono" });
export const metadata: Metadata = {
title: "BidWraith | AI Auction Intelligence",
description: "BidWraith is the smartest way to track global auction sites. Get real-time alerts and beat the competition.",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressHydrationWarning>
<body className={`${inter.className} ${jetbrainsMono.variable} bg-background text-foreground antialiased min-h-screen flex flex-col transition-colors duration-300`}>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{/* Fun tech subtle background grid */}
<div className="fixed inset-0 tech-grid -z-10 pointer-events-none" />
<Navbar />
<main className="flex-1 w-full flex flex-col relative overflow-hidden">
<PageTransition>
{children}
</PageTransition>
</main>
</ThemeProvider>
</body>
</html>
);
}

View File

@ -0,0 +1,121 @@
"use client";
import { motion } from 'framer-motion';
import { ExternalLink, Clock, Brain, Download, RefreshCw, ShoppingCart, Info } from 'lucide-react';
const mockListings = [
{ id: 1, title: 'Lenovo ThinkPad T14 Gen 3 AMD Ryzen 7', price: '$450.00', estValue: '$800.00', site: 'eBay US', time: '14m 30s', verdict: 'HIGH_OPPORTUNITY', image: '💻' },
{ id: 2, title: 'NVIDIA RTX 3080 Founders Edition', price: '$320.00', estValue: '$450.00', site: 'HiBid', time: '1h 12m', verdict: 'GOOD_DEAL', image: '🎮' },
{ id: 3, title: 'Lot of 10x iPhone 12 Pro 128GB Unlocked', price: '$1200.00', estValue: '$2500.00', site: 'ShopGoodwill', time: '4h 05m', verdict: 'RESELLER_DREAM', image: '📱' },
{ id: 4, title: 'Sony A7IV Mirrorless Camera Body', price: '$1500.00', estValue: '$1900.00', site: 'eBay UK', time: '5m 10s', verdict: 'URGENT', image: '📸' },
];
const containerVariants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 30, scale: 0.95 },
show: { opacity: 1, y: 0, scale: 1, transition: { type: "spring" as const, stiffness: 300, damping: 24 } }
};
export default function ListingsPage() {
return (
<div className="container mx-auto px-6 py-12 relative">
<div className="absolute top-[-20%] right-[-10%] w-[40%] h-[40%] bg-primary/10 rounded-full blur-[120px] pointer-events-none -z-10" />
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, ease: "easeOut" }}
className="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 gap-4"
>
<div>
<h1 className="text-5xl font-black tracking-tight mb-2 flex items-center gap-4 drop-shadow-sm">
Active Deals <motion.div whileHover={{ rotate: 15 }}><ShoppingCart className="w-10 h-10 text-primary drop-shadow-md" /></motion.div>
</h1>
<p className="text-muted-foreground text-xl font-medium">Live opportunities filtered by your AI agents.</p>
</div>
<div className="flex gap-3">
<motion.button
whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}
className="flex items-center gap-2 px-6 py-3 bg-secondary/80 backdrop-blur-md hover:bg-secondary border border-border/50 rounded-full font-bold transition-all shadow-sm"
>
<Download className="w-4 h-4" /> Export
</motion.button>
<motion.button
whileHover={{ scale: 1.05, boxShadow: "0 0 20px rgba(139, 92, 246, 0.4)" }} whileTap={{ scale: 0.95 }}
className="flex items-center gap-2 px-6 py-3 bg-primary text-primary-foreground rounded-full font-bold shadow-lg transition-all relative overflow-hidden group"
>
<span className="absolute inset-0 bg-white/20 translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-500" />
<RefreshCw className="w-4 h-4 group-hover:rotate-180 transition-transform duration-500" /> Refresh
</motion.button>
</div>
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
animate="show"
className="grid gap-6"
>
{mockListings.map((lot) => (
<motion.div
key={lot.id}
variants={itemVariants}
whileHover={{ y: -5, scale: 1.01 }}
className="flex flex-col md:flex-row gap-6 p-6 bg-card/80 backdrop-blur-xl border border-white/5 hover:border-primary/40 transition-colors rounded-3xl group shadow-lg hover:shadow-2xl relative overflow-hidden"
>
<div className="absolute top-0 left-0 w-2 h-full bg-gradient-to-b from-primary to-accent opacity-0 group-hover:opacity-100 transition-opacity" />
<div className="w-full md:w-64 h-48 bg-secondary/40 rounded-2xl flex flex-col items-center justify-center overflow-hidden relative border border-white/5 shadow-inner">
<motion.span
animate={{ y: [0, -10, 0] }}
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
className="text-7xl group-hover:scale-110 transition-transform duration-500 drop-shadow-xl"
>
{lot.image}
</motion.span>
<div className="absolute inset-0 bg-gradient-to-t from-primary/20 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
</div>
<div className="flex-1 flex flex-col justify-between py-2">
<div>
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-4 gap-2">
<h3 className="font-black text-3xl leading-tight group-hover:text-transparent group-hover:bg-clip-text group-hover:bg-gradient-to-r group-hover:from-primary group-hover:to-accent transition-all">{lot.title}</h3>
<div className={`px-5 py-2 text-sm font-black rounded-full whitespace-nowrap shadow-inner border ${lot.verdict === 'URGENT' ? 'bg-red-500/10 border-red-500/20 text-red-500' : lot.verdict === 'RESELLER_DREAM' ? 'bg-purple-500/10 border-purple-500/20 text-purple-500' : 'bg-green-500/10 border-green-500/20 text-green-500'}`}>
{lot.verdict.replace('_', ' ')}
</div>
</div>
<div className="flex flex-wrap items-center gap-4 text-sm font-bold text-muted-foreground mb-4">
<span className="flex items-center gap-2 bg-secondary/60 px-4 py-1.5 rounded-xl border border-white/5"><ExternalLink className="w-4 h-4" /> {lot.site}</span>
<span className="flex items-center gap-2 bg-amber-500/10 text-amber-500 px-4 py-1.5 rounded-xl border border-amber-500/20"><Clock className="w-4 h-4" /> Ends in {lot.time}</span>
<span className="flex items-center gap-2 bg-blue-500/10 text-blue-500 px-4 py-1.5 rounded-xl border border-blue-500/20"><Brain className="w-4 h-4" /> AI Verified</span>
</div>
</div>
<div className="flex flex-col sm:flex-row justify-between items-end gap-4 mt-4">
<div className="flex items-baseline gap-4">
<div className="text-5xl font-black text-foreground drop-shadow-md">{lot.price}</div>
<div className="text-sm font-black text-muted-foreground mb-1">
Est: <span className="line-through decoration-red-500 decoration-2">{lot.estValue}</span>
</div>
</div>
<motion.button
whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}
className="w-full sm:w-auto px-8 py-4 bg-secondary/80 hover:bg-primary hover:text-primary-foreground rounded-2xl font-black text-lg transition-all shadow-md group-hover:shadow-xl flex items-center justify-center gap-3 border border-white/5 group-hover:border-primary/50"
>
<Info className="w-5 h-5" /> Analyze Lot
</motion.button>
</div>
</div>
</motion.div>
))}
</motion.div>
</div>
);
}

227
frontend/app/page.tsx Normal file
View File

@ -0,0 +1,227 @@
"use client";
import { motion, useScroll, useTransform } from 'framer-motion';
import { Rocket, Target, Zap, ShieldCheck, Cpu, ChevronRight } from 'lucide-react';
import Link from 'next/link';
import dynamic from 'next/dynamic';
const Hero3D = dynamic(() => import('@/components/Hero3D'), { ssr: false });
const features = [
{
icon: Target,
title: "Always-On Radar",
description: "Monitors dozens of auction sites simultaneously. The engine never sleeps, so you never miss an ending lot.",
color: "text-blue-500",
bg: "bg-blue-500/10"
},
{
icon: Cpu,
title: "AI-Grade Filtering",
description: "External LLMs score and filter every lot against your precise custom targets. We drop the noise and show only true deals.",
color: "text-purple-500",
bg: "bg-purple-500/10"
},
{
icon: Zap,
title: "Real-Time Comms",
description: "Instant alerts delivered via Telegram, Discord, or Email the minute a high-value lot appears or is about to close.",
color: "text-yellow-500",
bg: "bg-yellow-500/10"
},
{
icon: ShieldCheck,
title: "Stealth Engine",
description: "Human-like browsing algorithms bypass standard detection, ensuring reliable intelligence gathering from any platform.",
color: "text-green-500",
bg: "bg-green-500/10"
}
];
export default function LandingPage() {
const { scrollYProgress } = useScroll();
const yHero = useTransform(scrollYProgress, [0, 1], ["0%", "50%"]);
const opacityHero = useTransform(scrollYProgress, [0, 0.2], [1, 0]);
return (
<div className="relative w-full min-h-screen bg-background text-foreground overflow-hidden font-sans">
{/* Dynamic 3D Background */}
<div className="fixed inset-0 w-full h-[100vh] pointer-events-none -z-20">
<Hero3D />
</div>
{/* Dynamic Background Blob Elements */}
<div className="fixed top-0 left-0 w-full h-full overflow-hidden -z-10 opacity-40 pointer-events-none mix-blend-screen">
<motion.div
animate={{
scale: [1, 1.2, 1],
rotate: [0, 90, 0],
}}
transition={{ duration: 20, repeat: Infinity, ease: "linear" }}
className="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] bg-primary/20 rounded-full blur-[140px]"
/>
<motion.div
animate={{
scale: [1, 1.5, 1],
rotate: [0, -90, 0],
}}
transition={{ duration: 25, repeat: Infinity, ease: "linear" }}
className="absolute bottom-[-10%] right-[-10%] w-[50%] h-[50%] bg-accent/20 rounded-full blur-[140px]"
/>
</div>
{/* Hero Section */}
<section className="container mx-auto px-6 pt-32 pb-32 text-center relative z-10 min-h-[90vh] flex flex-col justify-center">
<motion.div
style={{ y: yHero, opacity: opacityHero }}
initial={{ opacity: 0, y: 50, scale: 0.9 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={{ duration: 1, type: "spring" as const, bounce: 0.4 }}
className="max-w-5xl mx-auto flex flex-col items-center backdrop-blur-sm bg-background/30 p-12 rounded-[3rem] border border-white/10 shadow-2xl"
>
<motion.div
initial={{ opacity: 0, scale: 0 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.2, type: "spring" as const }}
className="mb-8 inline-flex items-center gap-3 px-6 py-2 rounded-full bg-secondary/80 backdrop-blur-md text-primary font-bold text-sm shadow-[0_0_30px_rgba(139,92,246,0.3)] border border-primary/20"
>
<span className="flex h-3 w-3 relative">
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75"></span>
<span className="relative inline-flex rounded-full h-3 w-3 bg-primary"></span>
</span>
System v5 Neural Engine Online
</motion.div>
<h1 className="text-6xl md:text-8xl font-black mb-8 tracking-tighter leading-[1.1]">
The AI Sniper That <br />
<span className="text-transparent bg-clip-text bg-gradient-to-br from-primary via-accent to-blue-500 drop-shadow-sm">Never Sleeps</span>
</h1>
<p className="text-xl md:text-2xl text-muted-foreground mb-12 max-w-3xl mx-auto leading-relaxed font-medium">
BidWraith watches every global auction site, scores every lot with deep AI, and alerts you the moment a real deal drops. Get the ultimate edge.
</p>
<div className="flex flex-col sm:flex-row gap-6 justify-center w-full sm:w-auto">
<Link href="/dashboard" className="group relative inline-flex items-center justify-center gap-3 bg-primary text-primary-foreground px-10 py-5 rounded-2xl font-bold text-xl transition-all hover:scale-105 shadow-[0_0_40px_rgba(139,92,246,0.5)] w-full sm:w-auto overflow-hidden">
<motion.span
className="absolute inset-0 bg-white/20"
initial={{ x: "-100%" }}
whileHover={{ x: "100%" }}
transition={{ duration: 0.6, ease: "easeInOut" }}
/>
<Rocket className="w-6 h-6 group-hover:-translate-y-1 group-hover:translate-x-1 transition-transform" />
Initialize Console
</Link>
<Link href="#features" className="group inline-flex items-center justify-center gap-3 bg-secondary/80 backdrop-blur-md hover:bg-secondary text-foreground border border-border px-10 py-5 rounded-2xl font-bold text-xl transition-all w-full sm:w-auto">
Explore Tech
<ChevronRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
</div>
</motion.div>
</section>
{/* Stats Strip with Scroll Reveal */}
<section className="py-12 relative z-10">
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.8 }}
className="container mx-auto px-6"
>
<div className="bg-secondary/60 backdrop-blur-2xl border border-white/10 rounded-3xl p-8 shadow-2xl relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-primary/10 via-transparent to-accent/10 pointer-events-none" />
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 text-center relative z-10">
{[
{ label: 'Uptime', value: '24/7', color: 'text-primary' },
{ label: 'Alert Latency', value: '< 1s', color: 'text-accent' },
{ label: 'API Endpoints', value: '42', color: 'text-blue-500' },
{ label: 'Stealth Rate', value: '100%', color: 'text-green-500' }
].map((stat, i) => (
<motion.div
key={i}
whileHover={{ scale: 1.1 }}
className="flex flex-col items-center justify-center p-4 cursor-default"
>
<motion.div
initial={{ scale: 0 }}
whileInView={{ scale: 1 }}
viewport={{ once: true }}
transition={{ type: "spring" as const, delay: i * 0.1 }}
className={`text-5xl md:text-6xl font-black mb-2 drop-shadow-md ${stat.color}`}
>
{stat.value}
</motion.div>
<div className="text-sm md:text-base font-bold text-muted-foreground uppercase tracking-widest">{stat.label}</div>
</motion.div>
))}
</div>
</div>
</motion.div>
</section>
{/* Features Section */}
<section id="features" className="py-32 relative z-10">
<div className="container mx-auto px-6">
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
className="text-center mb-24"
>
<h2 className="text-5xl md:text-7xl font-extrabold mb-6 tracking-tight">Core <span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-accent">Capabilities</span></h2>
<p className="text-muted-foreground text-2xl max-w-3xl mx-auto font-medium">Advanced architecture designed to give you an unfair advantage in the global market.</p>
</motion.div>
<div className="grid md:grid-cols-2 gap-10 max-w-6xl mx-auto">
{features.map((feature, idx) => (
<motion.div
key={idx}
initial={{ opacity: 0, y: 50, rotateX: -10 }}
whileInView={{ opacity: 1, y: 0, rotateX: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.6, delay: idx * 0.1, type: "spring" as const, bounce: 0.3 }}
whileHover={{ y: -10, scale: 1.02 }}
className="p-12 rounded-[2.5rem] bg-card/80 backdrop-blur-xl border border-white/5 hover:border-primary/30 transition-all shadow-xl hover:shadow-2xl group relative overflow-hidden"
>
<div className={`absolute top-0 right-0 w-64 h-64 ${feature.bg} rounded-full blur-[80px] -z-10 group-hover:scale-150 transition-transform duration-700`} />
<div className={`w-24 h-24 rounded-3xl ${feature.bg} flex items-center justify-center mb-8 group-hover:scale-110 group-hover:rotate-6 transition-transform duration-300 shadow-inner`}>
<feature.icon className={`w-12 h-12 ${feature.color}`} />
</div>
<h3 className="text-3xl font-black mb-4 tracking-tight">{feature.title}</h3>
<p className="text-muted-foreground text-xl leading-relaxed font-medium">
{feature.description}
</p>
</motion.div>
))}
</div>
</div>
</section>
{/* Footer CTA with 3D feel */}
<section className="py-32 text-center relative z-10 overflow-hidden">
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="container mx-auto px-6"
>
<div className="relative bg-gradient-to-br from-primary/30 via-background to-accent/30 border border-white/10 pt-32 pb-24 rounded-[4rem] shadow-2xl overflow-hidden backdrop-blur-md">
<motion.div
animate={{ rotate: 360 }}
transition={{ duration: 50, repeat: Infinity, ease: "linear" }}
className="absolute top-[-50%] left-[-20%] w-[100%] h-[150%] bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-20 mix-blend-overlay pointer-events-none"
/>
<div className="relative z-10">
<h2 className="text-6xl md:text-8xl font-black mb-12 tracking-tight drop-shadow-xl">Ready to dominate?</h2>
<Link href="/dashboard" className="group inline-flex items-center gap-4 bg-foreground text-background font-bold text-2xl px-14 py-7 rounded-full transition-all shadow-[0_20px_50px_rgba(0,0,0,0.3)] hover:shadow-[0_20px_50px_rgba(139,92,246,0.5)] hover:-translate-y-2">
Start Sniping Now
<Rocket className="w-8 h-8 group-hover:translate-x-2 group-hover:-translate-y-2 transition-transform" />
</Link>
</div>
</div>
</motion.div>
</section>
</div>
);
}

View File

@ -0,0 +1,177 @@
"use client";
import { motion } from 'framer-motion';
import { Save, Bell, Shield, Database, Cpu, Settings, MessageSquare, Sliders, HardDrive, Trash2 } from 'lucide-react';
const containerVariants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 20, scale: 0.95 },
show: { opacity: 1, y: 0, scale: 1, transition: { type: "spring" as const, stiffness: 300, damping: 24 } }
};
export default function SettingsPage() {
return (
<div className="container mx-auto px-6 py-12 relative">
<div className="absolute top-[20%] left-[-10%] w-[30%] h-[30%] bg-blue-500/10 rounded-full blur-[120px] pointer-events-none -z-10" />
<div className="absolute bottom-[10%] right-[10%] w-[30%] h-[30%] bg-purple-500/10 rounded-full blur-[120px] pointer-events-none -z-10" />
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
className="flex flex-col md:flex-row justify-between items-start md:items-center mb-12 gap-4"
>
<div>
<h1 className="text-5xl font-black tracking-tight mb-3 flex items-center gap-4 drop-shadow-sm">
System Config <motion.div animate={{ rotate: 360 }} transition={{ duration: 10, repeat: Infinity, ease: "linear" }}><Settings className="w-10 h-10 text-primary drop-shadow-md" /></motion.div>
</h1>
<p className="text-muted-foreground text-xl font-medium">Adjust core parameters, AI models, and notification routing.</p>
</div>
<motion.button
whileHover={{ scale: 1.05, boxShadow: "0 0 25px rgba(139, 92, 246, 0.4)" }}
whileTap={{ scale: 0.95 }}
className="flex items-center gap-3 px-8 py-4 bg-primary text-primary-foreground rounded-2xl font-black shadow-lg transition-all relative overflow-hidden group"
>
<span className="absolute inset-0 bg-white/20 translate-y-[100%] group-hover:translate-y-[0%] transition-transform duration-300" />
<Save className="w-5 h-5 relative z-10" />
<span className="relative z-10">Save Changes</span>
</motion.button>
</motion.div>
<motion.div
variants={containerVariants}
initial="hidden"
animate="show"
className="grid md:grid-cols-3 gap-8"
>
<div className="md:col-span-2 space-y-8">
{/* AI Settings */}
<motion.div variants={itemVariants} className="p-8 bg-card/80 backdrop-blur-xl border border-white/5 rounded-[2.5rem] shadow-xl relative overflow-hidden group hover:border-blue-500/30 transition-colors">
<div className="absolute -right-20 -top-20 w-48 h-48 bg-blue-500/10 rounded-full blur-[60px] group-hover:scale-150 transition-transform duration-700 pointer-events-none" />
<h2 className="text-3xl font-black mb-8 flex items-center gap-4 border-b border-border/50 pb-6 relative z-10">
<div className="p-3 bg-blue-500/10 rounded-2xl border border-blue-500/20 shadow-inner"><Cpu className="w-8 h-8 text-blue-500" /></div>
AI Engine Preferences
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 relative z-10">
<div>
<label className="block text-sm font-black text-muted-foreground mb-3 uppercase tracking-wider">Primary Provider</label>
<div className="relative">
<select className="w-full bg-secondary/50 border border-white/10 rounded-2xl px-5 py-4 text-lg font-bold focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/50 transition-all appearance-none shadow-inner cursor-pointer hover:bg-secondary/80">
<option>Anthropic (Claude 3.5 Sonnet)</option>
<option>OpenAI (GPT-4o)</option>
<option>Groq (Llama 3 70B)</option>
<option>Ollama (Local)</option>
</select>
<div className="absolute right-5 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground"></div>
</div>
</div>
<div>
<label className="block text-sm font-black text-muted-foreground mb-3 uppercase tracking-wider">Strictness Level</label>
<div className="relative">
<select className="w-full bg-secondary/50 border border-white/10 rounded-2xl px-5 py-4 text-lg font-bold focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/50 transition-all appearance-none shadow-inner cursor-pointer hover:bg-secondary/80">
<option>High (Miss deals, zero noise)</option>
<option selected>Balanced</option>
<option>Low (Catch everything, more noise)</option>
</select>
<div className="absolute right-5 top-1/2 -translate-y-1/2 pointer-events-none text-muted-foreground"></div>
</div>
</div>
<div className="col-span-1 md:col-span-2">
<label className="block text-sm font-black text-muted-foreground mb-3 uppercase tracking-wider">API Key</label>
<input type="password" value="sk-ant-api03-*************************" className="w-full bg-secondary/50 border border-white/10 rounded-2xl px-5 py-4 text-lg font-bold focus:outline-none focus:border-blue-500 focus:ring-1 focus:ring-blue-500/50 transition-all shadow-inner" readOnly/>
</div>
</div>
</motion.div>
{/* Alert Routing */}
<motion.div variants={itemVariants} className="p-8 bg-card/80 backdrop-blur-xl border border-white/5 rounded-[2.5rem] shadow-xl relative overflow-hidden group hover:border-amber-500/30 transition-colors">
<div className="absolute -left-20 -bottom-20 w-48 h-48 bg-amber-500/10 rounded-full blur-[60px] group-hover:scale-150 transition-transform duration-700 pointer-events-none" />
<h2 className="text-3xl font-black mb-8 flex items-center gap-4 border-b border-border/50 pb-6 relative z-10">
<div className="p-3 bg-amber-500/10 rounded-2xl border border-amber-500/20 shadow-inner"><Bell className="w-8 h-8 text-amber-500" /></div>
Alert Routing
</h2>
<div className="space-y-4 relative z-10">
<motion.div whileHover={{ scale: 1.01 }} className="flex items-center justify-between p-6 bg-secondary/40 border border-white/5 rounded-3xl hover:border-amber-500/50 transition-all cursor-pointer shadow-sm">
<div className="flex items-center gap-5">
<div className="p-4 bg-[#0088cc]/10 rounded-2xl border border-[#0088cc]/20"><MessageSquare className="w-8 h-8 text-[#0088cc]" /></div>
<div>
<div className="font-black text-xl">Telegram Bot</div>
<div className="text-base font-bold text-green-500 mt-1 flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" /> Connected (@BidWraithBot)
</div>
</div>
</div>
<div className="w-16 h-8 bg-amber-500 rounded-full relative shadow-[0_0_15px_rgba(245,158,11,0.4)]">
<motion.div layout className="absolute right-1 top-1 w-6 h-6 bg-white rounded-full shadow-md"></motion.div>
</div>
</motion.div>
<motion.div whileHover={{ scale: 1.01 }} className="flex items-center justify-between p-6 bg-secondary/40 border border-white/5 rounded-3xl hover:border-border/80 transition-all cursor-pointer shadow-sm opacity-70">
<div className="flex items-center gap-5">
<div className="p-4 bg-[#5865F2]/10 rounded-2xl border border-[#5865F2]/20"><MessageSquare className="w-8 h-8 text-[#5865F2]" /></div>
<div>
<div className="font-black text-xl">Discord Webhook</div>
<div className="text-base font-bold text-muted-foreground mt-1">Not Configured</div>
</div>
</div>
<div className="w-16 h-8 bg-secondary-foreground/20 rounded-full relative shadow-inner">
<motion.div layout className="absolute left-1 top-1 w-6 h-6 bg-card rounded-full shadow-sm"></motion.div>
</div>
</motion.div>
</div>
</motion.div>
</div>
<div className="space-y-8">
{/* Stealth Mode */}
<motion.div variants={itemVariants} className="p-8 bg-card/80 backdrop-blur-xl border border-white/5 rounded-[2.5rem] shadow-xl relative overflow-hidden group hover:border-purple-500/30 transition-colors">
<div className="absolute -right-10 -bottom-10 w-40 h-40 bg-purple-500/10 rounded-full blur-[50px] group-hover:scale-150 transition-transform duration-700 pointer-events-none" />
<h2 className="text-3xl font-black mb-8 flex items-center gap-4 border-b border-border/50 pb-6 relative z-10">
<div className="p-3 bg-purple-500/10 rounded-2xl border border-purple-500/20 shadow-inner"><Shield className="w-8 h-8 text-purple-500" /></div>
Stealth Mode
</h2>
<div className="space-y-8 relative z-10">
<div>
<label className="block text-sm font-black text-muted-foreground mb-4 uppercase tracking-wider">Humanization Level</label>
<input type="range" min="1" max="100" defaultValue="75" className="w-full accent-purple-500 h-3 bg-secondary/80 rounded-full appearance-none cursor-pointer shadow-inner" />
<div className="flex justify-between text-sm font-bold text-muted-foreground mt-4">
<span className="bg-secondary px-3 py-1 rounded-lg">Fast / Bot</span>
<span className="bg-secondary px-3 py-1 rounded-lg text-purple-500">Slow / Human</span>
</div>
</div>
<div className="pt-2">
<label className="flex items-center justify-between p-5 bg-secondary/40 border border-white/5 rounded-2xl hover:bg-secondary/60 transition-colors cursor-pointer group">
<span className="text-lg font-black group-hover:text-purple-500 transition-colors">Use Proxy Rotation</span>
<input type="checkbox" defaultChecked className="w-6 h-6 accent-purple-500 rounded text-purple-500 focus:ring-purple-500 bg-card border-white/10" />
</label>
</div>
</div>
</motion.div>
{/* Data Management */}
<motion.div variants={itemVariants} className="p-8 bg-card/80 backdrop-blur-xl border border-white/5 rounded-[2.5rem] shadow-xl relative overflow-hidden group hover:border-red-500/30 transition-colors">
<h2 className="text-3xl font-black mb-8 flex items-center gap-4 border-b border-border/50 pb-6 relative z-10">
<div className="p-3 bg-red-500/10 rounded-2xl border border-red-500/20 shadow-inner"><HardDrive className="w-8 h-8 text-red-500" /></div>
Data Management
</h2>
<div className="space-y-4 relative z-10">
<motion.button whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} className="w-full py-4 bg-secondary/60 hover:bg-secondary border border-white/5 hover:border-red-500/50 text-lg font-black rounded-2xl transition-all flex items-center justify-center gap-3 text-foreground">
<Database className="w-5 h-5 text-red-500" /> Export Database
</motion.button>
<motion.button whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} className="w-full py-4 bg-red-500/10 hover:bg-red-500/20 border border-red-500/20 text-lg font-black rounded-2xl transition-all flex items-center justify-center gap-3 text-red-500">
<Trash2 className="w-5 h-5" /> Purge Old Logs
</motion.button>
</div>
</motion.div>
</div>
</motion.div>
</div>
);
}

107
frontend/app/sites/page.tsx Normal file
View File

@ -0,0 +1,107 @@
"use client";
import { motion } from 'framer-motion';
import { ShieldCheck, ShieldAlert, Cpu, Network, PauseCircle, PlayCircle, Plus } from 'lucide-react';
const mockSites = [
{ id: 1, name: 'eBay US', url: 'ebay.com', status: 'healthy', lag: '240ms', lastScrape: '2s ago' },
{ id: 2, name: 'eBay UK', url: 'ebay.co.uk', status: 'healthy', lag: '310ms', lastScrape: '5s ago' },
{ id: 3, name: 'HiBid', url: 'hibid.com', status: 'warning', lag: '1200ms', lastScrape: '1m ago' },
{ id: 4, name: 'ShopGoodwill', url: 'shopgoodwill.com', status: 'healthy', lag: '450ms', lastScrape: '12s ago' },
];
export default function SitesPage() {
return (
<div className="container mx-auto px-6 py-12">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 gap-4">
<div>
<h1 className="text-4xl font-extrabold tracking-tight mb-2 flex items-center gap-3">
Network Nodes <span className="text-primary"><Network className="w-8 h-8" /></span>
</h1>
<p className="text-muted-foreground text-lg">Manage and monitor the auction platforms your agents are scraping.</p>
</div>
<button className="flex items-center gap-2 px-6 py-3 bg-secondary hover:bg-secondary/80 border-2 border-border/50 text-foreground transition-all rounded-full font-bold shadow-sm hover:shadow-md hover:-translate-y-0.5">
<Plus className="w-5 h-5" /> Add New Site
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{mockSites.map((site, i) => (
<motion.div
key={site.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.1, type: "spring" }}
className={`p-6 rounded-3xl border-2 shadow-sm hover:shadow-xl transition-all hover:-translate-y-1 ${site.status === 'healthy' ? 'bg-card border-border/50' : 'bg-amber-500/5 border-amber-500/30'}`}
>
<div className="flex justify-between items-start mb-6">
<div>
<h3 className="font-bold text-xl mb-1">{site.name}</h3>
<div className="text-sm font-medium text-muted-foreground bg-secondary/50 px-2 py-0.5 rounded-md inline-block">{site.url}</div>
</div>
<div className={`p-2 rounded-xl ${site.status === 'healthy' ? 'bg-green-500/10' : 'bg-amber-500/10'}`}>
{site.status === 'healthy' ? (
<ShieldCheck className="w-6 h-6 text-green-500" />
) : (
<ShieldAlert className="w-6 h-6 text-amber-500 animate-pulse" />
)}
</div>
</div>
<div className="space-y-4 font-medium text-sm">
<div className="flex justify-between items-center p-3 bg-secondary/30 rounded-xl">
<span className="text-muted-foreground">Latency</span>
<span className={`font-bold ${site.status === 'healthy' ? 'text-foreground' : 'text-amber-500'}`}>{site.lag}</span>
</div>
<div className="flex justify-between items-center p-3 bg-secondary/30 rounded-xl">
<span className="text-muted-foreground">Last Ping</span>
<span className="font-bold">{site.lastScrape}</span>
</div>
<div className="flex justify-between items-center p-3 bg-secondary/30 rounded-xl">
<span className="text-muted-foreground">Selectors</span>
<span className="font-bold text-green-500">Verified</span>
</div>
</div>
<div className="mt-6 flex gap-3">
<button className="flex-1 py-2.5 bg-secondary hover:bg-primary hover:text-primary-foreground rounded-xl text-sm font-bold transition-colors flex items-center justify-center gap-2">
<Cpu className="w-4 h-4" /> Inspect
</button>
<button className="p-2.5 bg-secondary hover:bg-amber-500/10 text-muted-foreground hover:text-amber-500 rounded-xl transition-colors flex items-center justify-center">
<PauseCircle className="w-5 h-5" />
</button>
</div>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.4 }}
className="mt-12 p-8 bg-gradient-to-br from-primary/10 to-accent/5 border-2 border-primary/20 rounded-3xl"
>
<div className="flex items-center gap-3 mb-4">
<div className="p-3 bg-primary/20 rounded-2xl">
<Cpu className="w-6 h-6 text-primary" />
</div>
<h3 className="text-2xl font-bold">Auto-Adaptation Engine</h3>
</div>
<p className="text-lg text-muted-foreground max-w-3xl mb-6 font-medium">
The engine can autonomously learn new site structures. Provide a target URL, and the Scout Agent will attempt to generate valid selectors for listings, prices, and countdown timers.
</p>
<div className="flex flex-col sm:flex-row gap-4">
<input
type="text"
placeholder="https://example-auction.com"
className="flex-1 bg-background/80 backdrop-blur-sm border-2 border-border/50 rounded-2xl px-6 py-4 text-lg focus:outline-none focus:border-primary transition-colors shadow-inner"
/>
<button className="px-8 py-4 bg-primary text-primary-foreground font-bold text-lg rounded-2xl hover:bg-primary/90 transition-all shadow-lg hover:shadow-primary/30 flex items-center justify-center gap-2 hover:-translate-y-0.5 active:translate-y-0">
<PlayCircle className="w-6 h-6" />
Initiate Scan
</button>
</div>
</motion.div>
</div>
);
}

View File

@ -0,0 +1,106 @@
"use client";
import { useRef, useState } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import {
Float,
MeshDistortMaterial,
Environment,
ContactShadows,
Lightformer,
PerspectiveCamera
} from "@react-three/drei";
import * as THREE from "three";
import { useTheme } from "next-themes";
function FloatingShape() {
const mesh = useRef<THREE.Mesh>(null);
const { theme, resolvedTheme } = useTheme();
const [hovered, setHovered] = useState(false);
const currentTheme = theme === "system" ? resolvedTheme : theme;
const isDark = currentTheme === "dark";
const color = isDark ? "#6366f1" : "#8b5cf6"; // primary or accent colors
const emissive = isDark ? "#3730a3" : "#4c1d95";
// Animate on interaction
useFrame((state) => {
if (mesh.current) {
mesh.current.rotation.x = THREE.MathUtils.lerp(mesh.current.rotation.x, hovered ? state.mouse.y * 0.5 : state.clock.getElapsedTime() * 0.2, 0.1);
mesh.current.rotation.y = THREE.MathUtils.lerp(mesh.current.rotation.y, hovered ? state.mouse.x * 0.5 : state.clock.getElapsedTime() * 0.3, 0.1);
}
});
return (
<Float
speed={4}
rotationIntensity={1}
floatIntensity={2}
>
<mesh
ref={mesh}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
scale={hovered ? 1.1 : 1}
>
<torusKnotGeometry args={[1.2, 0.4, 256, 64]} />
<MeshDistortMaterial
color={color}
envMapIntensity={isDark ? 0.8 : 0.4}
clearcoat={0.9}
clearcoatRoughness={0.1}
metalness={0.9}
roughness={0.1}
distort={0.4}
speed={3}
emissive={emissive}
emissiveIntensity={0.2}
/>
</mesh>
</Float>
);
}
export default function Hero3D() {
const { theme, resolvedTheme } = useTheme();
const currentTheme = theme === "system" ? resolvedTheme : theme;
const isDark = currentTheme === "dark";
return (
<div className="absolute inset-0 w-full h-full -z-10 opacity-70">
<Canvas shadows camera={{ position: [0, 0, 5], fov: 45 }}>
<PerspectiveCamera makeDefault position={[0, 0, 5]} />
<ambientLight intensity={isDark ? 0.5 : 0.8} />
<spotLight
position={[10, 10, 10]}
angle={0.15}
penumbra={1}
intensity={isDark ? 1 : 1.5}
castShadow
/>
<pointLight position={[-10, -10, -10]} intensity={0.5} color="#8b5cf6" />
<FloatingShape />
<Environment resolution={256}>
<group rotation={[-Math.PI / 3, 0, 1]}>
<Lightformer form="circle" intensity={4} rotation-x={Math.PI / 2} position={[0, 5, -9]} scale={2} />
<Lightformer form="circle" intensity={2} rotation-y={Math.PI / 2} position={[-5, 1, -1]} scale={2} />
<Lightformer form="circle" intensity={2} rotation-y={Math.PI / 2} position={[5, 1, -1]} scale={2} />
<Lightformer form="circle" intensity={2} rotation-y={-Math.PI / 2} position={[10, 1, 0]} scale={8} />
</group>
</Environment>
<ContactShadows
position={[0, -2, 0]}
opacity={isDark ? 0.4 : 0.2}
scale={20}
blur={2}
far={4.5}
/>
</Canvas>
</div>
);
}

View File

@ -0,0 +1,80 @@
"use client";
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { LayoutDashboard, List, Search, Globe, Settings, TerminalSquare, Rocket } from 'lucide-react';
import { motion } from 'framer-motion';
import { ThemeToggle } from './theme-toggle';
const navItems = [
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard },
{ name: 'Listings', href: '/listings', icon: List },
{ name: 'Targets', href: '/keywords', icon: Search },
{ name: 'Sites', href: '/sites', icon: Globe },
{ name: 'AI Log', href: '/ai-log', icon: TerminalSquare },
{ name: 'Settings', href: '/settings', icon: Settings },
];
export default function Navbar() {
const pathname = usePathname();
return (
<motion.header
initial={{ y: -100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.8, type: "spring" as const, bounce: 0.4 }}
className="sticky top-0 z-50 w-full border-b border-border/40 bg-background/70 backdrop-blur-2xl transition-all shadow-sm"
>
<div className="container mx-auto flex h-16 items-center justify-between px-4">
<Link href="/" className="flex items-center gap-2 group">
<motion.div
whileHover={{ scale: 1.1, rotate: 10 }}
whileTap={{ scale: 0.9 }}
className="bg-primary/10 p-2 rounded-xl transition-colors duration-300"
>
<Rocket className="h-6 w-6 text-primary group-hover:fill-primary/20 transition-all" />
</motion.div>
<span className="font-bold text-xl tracking-tight text-foreground group-hover:text-primary transition-colors">
BidWraith
</span>
</Link>
<nav className="hidden md:flex gap-1 bg-secondary/30 p-1.5 rounded-full border border-white/5">
{navItems.map((item, idx) => {
const isActive = pathname.startsWith(item.href);
return (
<Link
key={item.name}
href={item.href}
className={`relative flex items-center gap-2 px-4 py-2 rounded-full text-sm font-bold transition-all duration-300 ${
isActive ? 'text-primary-foreground shadow-md' : 'text-muted-foreground hover:text-foreground hover:bg-secondary/80'
}`}
>
{isActive && (
<motion.div
layoutId="nav-pill"
className="absolute inset-0 bg-primary rounded-full -z-10 shadow-[0_0_15px_rgba(139,92,246,0.4)]"
transition={{ type: 'spring', stiffness: 400, damping: 30 }}
/>
)}
<item.icon className={`h-4 w-4 ${isActive ? 'text-primary-foreground' : ''}`} />
{item.name}
</Link>
);
})}
</nav>
<div className="flex items-center gap-4">
<ThemeToggle />
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{ delay: 0.5, type: "spring" as const }}
className="hidden lg:flex items-center gap-2 px-4 py-1.5 rounded-full bg-secondary text-xs font-bold text-primary shadow-inner border border-primary/20"
>
<span className="w-2.5 h-2.5 rounded-full bg-green-500 animate-pulse shadow-[0_0_10px_rgba(34,197,94,0.8)]"></span>
SYSTEM_ACTIVE
</motion.div>
</div>
</div>
</motion.header>
);
}

View File

@ -0,0 +1,22 @@
"use client";
import { motion } from "framer-motion";
import { usePathname } from "next/navigation";
import { ReactNode } from "react";
export default function PageTransition({ children }: { children: ReactNode }) {
const pathname = usePathname();
return (
<motion.div
key={pathname}
initial={{ opacity: 0, y: 20, filter: 'blur(10px)' }}
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
exit={{ opacity: 0, y: -20, filter: 'blur(10px)' }}
transition={{ duration: 0.4, type: "spring" as const, bounce: 0.2 }}
className="flex-1 w-full flex flex-col"
>
{children}
</motion.div>
);
}

View File

@ -0,0 +1,11 @@
"use client"
import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}

View File

@ -0,0 +1,20 @@
"use client"
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
export function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<button
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="relative inline-flex h-9 w-9 items-center justify-center rounded-md bg-secondary/20 hover:bg-secondary/40 text-secondary-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
aria-label="Toggle theme"
>
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 text-amber-500" />
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 text-blue-400" />
</button>
)
}

17
frontend/dev.log Normal file
View File

@ -0,0 +1,17 @@
> frontend@0.1.0 dev
> next dev
⚠ Warning: Next.js inferred your workspace root, but it may not be correct.
We detected multiple lockfiles and selected the directory of /home/ubuntu/executor/package-lock.json as the root directory.
To silence this warning, set `turbopack.root` in your Next.js config, or consider removing one of the lockfiles if it's not needed.
See https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopack#root-directory for more information.
Detected additional lockfiles:
* /home/ubuntu/executor/workspace/frontend/package-lock.json
▲ Next.js 16.1.6 (Turbopack)
- Local: http://localhost:3000
- Network: http://10.128.0.61:3000
✓ Starting...
✓ Ready in 2.1s

File diff suppressed because it is too large Load Diff

View File

@ -24,23 +24,25 @@
"@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-toast": "^1.2.15", "@radix-ui/react-toast": "^1.2.15",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.5.0",
"@tanstack/react-query": "^5.90.21", "@tanstack/react-query": "^5.90.21",
"@types/three": "^0.183.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.36.0",
"lucide-react": "^0.577.0",
"motion": "^12.36.0", "motion": "^12.36.0",
"next": "16.1.6", "next": "16.1.6",
"next-themes": "^0.4.6",
"radix-ui": "^1.4.3", "radix-ui": "^1.4.3",
"react": "19.2.3", "react": "19.2.3",
"react-dom": "19.2.3", "react-dom": "19.2.3",
"shadcn": "^4.0.6", "shadcn": "^4.0.6",
"tailwind-merge": "^3.5.0", "three": "^0.183.2",
"three-stdlib": "^2.36.1",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"zustand": "^5.0.11" "zustand": "^5.0.11"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4.2.2",
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.2", "@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
@ -48,10 +50,16 @@
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@vitejs/plugin-react": "^5.1.4", "@vitejs/plugin-react": "^5.1.4",
"autoprefixer": "^10.4.27",
"clsx": "^2.1.1",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.1.6", "eslint-config-next": "16.1.6",
"framer-motion": "^12.38.0",
"jsdom": "^28.1.0", "jsdom": "^28.1.0",
"tailwindcss": "^4", "lucide-react": "^0.577.0",
"postcss": "^8.5.8",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.0.0",
"typescript": "^5", "typescript": "^5",
"vitest": "^4.0.18" "vitest": "^4.0.18"
} }

View File

@ -1 +0,0 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 391 B

View File

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

Before

Width:  |  Height:  |  Size: 128 B

View File

@ -1 +0,0 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

Before

Width:  |  Height:  |  Size: 385 B

View File

@ -1,7 +1,7 @@
""" """
Ghost Node Worker Ghost Node Worker
Three-thread architecture: Three-thread architecture:
Thread A FastAPI dashboard (port 7000) Thread A FastAPI dashboard (port 3001)
Thread B Async Playwright scraper (nuclear_engine) Thread B Async Playwright scraper (nuclear_engine)
Thread C Telegram C2 polling loop Thread C Telegram C2 polling loop
""" """
@ -4586,6 +4586,7 @@ async def nuclear_engine() -> None:
_boost_secs = int(_get_config("boost_interval_mins", "2")) * 60 _boost_secs = int(_get_config("boost_interval_mins", "2")) * 60
_db_boost = SessionLocal() _db_boost = SessionLocal()
try: try:
from datetime import timedelta
_soon_cutoff = datetime.now() + timedelta(minutes=30) _soon_cutoff = datetime.now() + timedelta(minutes=30)
_closing_soon = _db_boost.query(Listing).filter( _closing_soon = _db_boost.query(Listing).filter(
Listing.time_left_mins != None, Listing.time_left_mins != None,
@ -5497,12 +5498,12 @@ def engine_restart():
then spawns a brand-new Python process running the same script with then spawns a brand-new Python process running the same script with
the same arguments via subprocess.Popen. the same arguments via subprocess.Popen.
3. After spawning the child, the current process calls os._exit(0) to 3. After spawning the child, the current process calls os._exit(0) to
terminate itself immediately and release port 7000. terminate itself immediately and release port 3001.
Why not os.execv? Why not os.execv?
os.execv works on Linux but on Windows it does NOT replace the current os.execv works on Linux but on Windows it does NOT replace the current
process it creates a new one while the old one keeps running, which process it creates a new one while the old one keeps running, which
causes an "address already in use" error on port 7000. causes an "address already in use" error on port 3001.
Why subprocess.Popen + os._exit(0)? Why subprocess.Popen + os._exit(0)?
Popen detaches the child before the parent exits, so the child is Popen detaches the child before the parent exits, so the child is
@ -5527,7 +5528,7 @@ def engine_restart():
except Exception as exc: except Exception as exc:
print(f"[GhostNode] ❌ Restart failed: {exc}") print(f"[GhostNode] ❌ Restart failed: {exc}")
return return
# Kill this process immediately — port 7000 is now free for the child # Kill this process immediately — port 3001 is now free for the child
os._exit(0) os._exit(0)
threading.Thread(target=_do_restart, daemon=True, name="GhostNode-Restart").start() threading.Thread(target=_do_restart, daemon=True, name="GhostNode-Restart").start()
@ -6035,7 +6036,7 @@ if __name__ == "__main__":
) )
closing_thread.start() closing_thread.start()
print("[GhostNode] 🕵️ Ghost Node online — Dashboard → http://localhost:7000") print("[GhostNode] 🕵️ Ghost Node online — Dashboard → http://localhost:3001")
# Thread A (FastAPI via uvicorn — blocks main thread) # Thread A (FastAPI via uvicorn — blocks main thread)
uvicorn.run(app, host="0.0.0.0", port=7000, log_level="warning") uvicorn.run(app, host="0.0.0.0", port=3001, log_level="warning")