forked from emre/www_projectmycelium_com
- Changed vertical translate classes to only apply on large screens (lg:) across agent animations (AgentCoordination, DeterministicExecution, Fungistor, Herodb, MOSSandboxes, MyceliumMesh) - Changed NoCentral home animation from -translate-y-10 to lg:-translate-y-10 - Updated pods animations with mobile-first approach: Connectivity (-translate-y-12 mobile, lg:translate-y-0), DataControl (maintains -translate-y-12 on
177 lines
5.1 KiB
TypeScript
177 lines
5.1 KiB
TypeScript
"use client";
|
|
|
|
import { motion, useReducedMotion } from "framer-motion";
|
|
import clsx from "clsx";
|
|
|
|
type Props = {
|
|
className?: string;
|
|
accent?: string;
|
|
bg?: string;
|
|
gridStroke?: string;
|
|
};
|
|
|
|
const W = 720; // 4:3 width
|
|
const H = 540; // 4:3 height
|
|
|
|
export default function Connectivity({
|
|
className,
|
|
accent = "#00b8db",
|
|
bg = "#0b0b0b",
|
|
gridStroke = "#2b2a2a",
|
|
}: Props) {
|
|
const prefers = useReducedMotion();
|
|
|
|
// Hex-like P2P topology — all peers connect *directly*
|
|
const pods = [
|
|
{ x: 250, y: 160 },
|
|
{ x: 470, y: 160 },
|
|
{ x: 580, y: 290 },
|
|
{ x: 470, y: 420 },
|
|
{ x: 250, y: 420 },
|
|
{ x: 140, y: 290 },
|
|
];
|
|
|
|
// Every pod connects to every other pod = full direct mesh
|
|
const links: [number, number][] = [];
|
|
for (let i = 0; i < pods.length; i++) {
|
|
for (let j = i + 1; j < pods.length; j++) links.push([i, j]);
|
|
}
|
|
|
|
const line = (i: number, j: number) => {
|
|
const a = pods[i], b = pods[j];
|
|
return `M ${a.x} ${a.y} L ${b.x} ${b.y}`;
|
|
};
|
|
|
|
return (
|
|
<div className={clsx("relative overflow-hidden", className)}>
|
|
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full lg:translate-y-0 -translate-y-12" style={{ background: bg }}>
|
|
|
|
{/* ========= GRID + FILTERS ========= */}
|
|
<defs>
|
|
<pattern id="conn-grid" width="28" height="28" patternUnits="userSpaceOnUse">
|
|
<path
|
|
d="M 28 0 L 0 0 0 28"
|
|
fill="none"
|
|
stroke={gridStroke}
|
|
strokeWidth="1"
|
|
opacity="0.35"
|
|
/>
|
|
</pattern>
|
|
|
|
<filter id="conn-glow">
|
|
<feGaussianBlur stdDeviation="3" result="b" />
|
|
<feMerge>
|
|
<feMergeNode in="b" />
|
|
<feMergeNode in="SourceGraphic" />
|
|
</feMerge>
|
|
</filter>
|
|
|
|
{/* Cyan soft gradient for pods */}
|
|
<radialGradient id="conn-pod" cx="50%" cy="50%" r="60%">
|
|
<stop offset="0%" stopColor={accent} stopOpacity="0.9" />
|
|
<stop offset="100%" stopColor={accent} stopOpacity="0.06" />
|
|
</radialGradient>
|
|
</defs>
|
|
|
|
<rect width={W} height={H} fill="url(#conn-grid)" />
|
|
|
|
{/* ========= BASE CONNECTION LINES (Full Mesh) ========= */}
|
|
{links.map(([i, j], idx) => (
|
|
<motion.path
|
|
key={`base-${idx}`}
|
|
d={line(i, j)}
|
|
stroke="#1f1f1f"
|
|
strokeWidth={3}
|
|
strokeLinecap="round"
|
|
fill="none"
|
|
initial={{ pathLength: 0, opacity: 0.2 }}
|
|
animate={{ pathLength: 1, opacity: 0.5 }}
|
|
transition={{ duration: 0.6, delay: idx * 0.03 }}
|
|
/>
|
|
))}
|
|
|
|
{/* ========= ACCENT DASH (Active P2P links) ========= */}
|
|
{links.map(([i, j], idx) => (
|
|
<motion.path
|
|
key={`dash-${idx}`}
|
|
d={line(i, j)}
|
|
stroke={accent}
|
|
strokeWidth={2}
|
|
fill="none"
|
|
strokeDasharray="12 10"
|
|
initial={{ pathLength: 0, opacity: 0.9 }}
|
|
animate={{ pathLength: 1, opacity: [0.9, 0.6, 0.9] }}
|
|
transition={{
|
|
duration: 1,
|
|
delay: idx * 0.04,
|
|
repeat: Infinity,
|
|
repeatType: "reverse",
|
|
}}
|
|
filter="url(#conn-glow)"
|
|
/>
|
|
))}
|
|
|
|
{/* ========= MOVING SIGNALS (Direct P2P) ========= */}
|
|
{!prefers &&
|
|
links.map(([i, j], idx) => (
|
|
<motion.circle
|
|
key={`pulse-${idx}`}
|
|
r={5}
|
|
fill={accent}
|
|
style={{ offsetPath: `path('${line(i, j)}')` }}
|
|
initial={{ offsetDistance: "0%", opacity: 0 }}
|
|
animate={{
|
|
offsetDistance: ["0%", "100%"],
|
|
opacity: [0.2, 1, 0.2],
|
|
}}
|
|
transition={{
|
|
duration: 2.3,
|
|
delay: idx * 0.2,
|
|
repeat: Infinity,
|
|
ease: "linear",
|
|
}}
|
|
filter="url(#conn-glow)"
|
|
/>
|
|
))}
|
|
|
|
{/* ========= CLOUD PODS ========= */}
|
|
{pods.map((p, i) => (
|
|
<g key={`pod-${i}`}>
|
|
{/* Outer soft cyan aura */}
|
|
{!prefers && (
|
|
<motion.ellipse
|
|
cx={p.x}
|
|
cy={p.y}
|
|
rx={42}
|
|
ry={28}
|
|
fill="none"
|
|
stroke={accent}
|
|
strokeWidth={2}
|
|
opacity={0.15}
|
|
animate={{ scale: [1, 1.1, 1] , opacity: [0.1, 0.25, 0.1] }}
|
|
transition={{ duration: 2.3, delay: i * 0.15, repeat: Infinity }}
|
|
filter="url(#conn-glow)"
|
|
/>
|
|
)}
|
|
|
|
{/* Pod body */}
|
|
<ellipse
|
|
cx={p.x}
|
|
cy={p.y}
|
|
rx={36}
|
|
ry={24}
|
|
fill="url(#conn-pod)"
|
|
stroke="#1f1f1f"
|
|
strokeWidth={2}
|
|
filter="url(#conn-glow)"
|
|
/>
|
|
|
|
{/* Inner dark core */}
|
|
<ellipse cx={p.x} cy={p.y} rx={18} ry={12} fill="#0f0f0f" stroke="#292929" strokeWidth={1.5} />
|
|
</g>
|
|
))}
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|