// components.jsx — shared: logos, icons, attack map, animated counters function Logo({ height = 28 }) { return WP Guardian; } function ShieldIcon({ size = 16, color = 'currentColor' }) { return ( ); } function CheckIcon({ size = 16, color = '#22c55e' }) { return ( ); } function ArrowIcon({ size = 14 }) { return ( ); } // Counts up to target whenever component appears in viewport function CountUp({ to, duration = 1800, format = (n) => Math.round(n).toLocaleString('es') , suffix = '', className = '' }) { const [val, setVal] = React.useState(0); const ref = React.useRef(null); const started = React.useRef(false); React.useEffect(() => { const el = ref.current; if (!el) return; const obs = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting && !started.current) { started.current = true; const start = performance.now(); const tick = (now) => { const t = Math.min(1, (now - start) / duration); const eased = 1 - Math.pow(1 - t, 3); setVal(eased * to); if (t < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); } }); }, { threshold: 0.4 }); obs.observe(el); return () => obs.disconnect(); }, [to, duration]); return {format(val)}{suffix}; } // Background world map shape (SVG silhouette + animated threat blips) function AttackMap() { // A handful of "incidents" positioned roughly across continents const blips = React.useMemo(() => [ { x: '18%', y: '38%' }, { x: '22%', y: '52%' }, { x: '12%', y: '46%' }, { x: '32%', y: '34%' }, { x: '46%', y: '32%' }, { x: '54%', y: '38%' }, { x: '64%', y: '36%' }, { x: '72%', y: '44%' }, { x: '78%', y: '52%' }, { x: '82%', y: '36%' }, { x: '40%', y: '62%' }, { x: '60%', y: '60%' }, { x: '28%', y: '64%' }, { x: '88%', y: '60%' }, { x: '14%', y: '60%' }, ], []); return ( ); } // Streaming "security events" terminal function ThreatTerminal({ height = 360 }) { const seedLines = React.useMemo(() => ([ { kind: 'block', ip: '185.92.221.14', msg: 'admin brute-force blocked', loc: 'RU' }, { kind: 'block', ip: '103.45.118.220', msg: 'wp-login.php → 412 attempts', loc: 'CN' }, { kind: 'warn', ip: '—', msg: 'CVE-2026-2418 detected (Elementor < 3.8.1)' }, { kind: 'ok', ip: '—', msg: 'backup snapshot saved → s3://wpg/2026-05-14' }, { kind: 'info', ip: '—', msg: 'plugin scan complete · 14 outdated · 2 critical' }, { kind: 'block', ip: '45.137.21.88', msg: 'xmlrpc.php flood blocked', loc: 'NL' }, { kind: 'ok', ip: '—', msg: 'WordPress core 6.9.3 → 6.9.4 ✓' }, { kind: 'warn', ip: '—', msg: 'AIO Backup last run 13d ago — escalating' }, { kind: 'block', ip: '94.232.41.7', msg: 'malicious file upload rejected (php in /uploads)', loc: 'UA' }, { kind: 'info', ip: '—', msg: 'Wordfence rules synced · 1,284 active' }, { kind: 'block', ip: '198.235.24.12', msg: 'SQLi attempt on ?s= parameter', loc: 'US' }, { kind: 'ok', ip: '—', msg: 'PHP 8.4.19 OK · WP-CLI 2.12.0 OK' }, { kind: 'warn', ip: '—', msg: 'theme "Newspaper" outdated · vendor pending' }, { kind: 'block', ip: '77.83.36.220', msg: 'enumeration probe on /author=1', loc: 'BG' }, ]), []); const [lines, setLines] = React.useState(() => seedLines.slice(0, 9)); React.useEffect(() => { let i = 9; const id = setInterval(() => { setLines(prev => { const next = [...prev, seedLines[i % seedLines.length]]; i++; return next.slice(-9); }); }, 1600); return () => clearInterval(id); }, [seedLines]); const now = () => { const d = new Date(); return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`; }; return (
wpguardian · live security log LIVE
{lines.map((l, i) => (
[{now()}] {l.kind === 'block' ? 'BLOCK' : l.kind === 'warn' ? 'ALERT' : l.kind === 'ok' ? 'OK' : 'INFO'} {l.ip !== '—' && {l.ip}} {l.loc && ·{l.loc}} {l.msg}
))}
); } Object.assign(window, { Logo, ShieldIcon, CheckIcon, ArrowIcon, CountUp, AttackMap, ThreatTerminal });