119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
|
|
'use client'
|
||
|
|
|
||
|
|
import { motion } from 'framer-motion'
|
||
|
|
|
||
|
|
const nodes = [
|
||
|
|
{ id: 'fortran', label: 'Fortran IV', x: 20, y: 15, era: '1984' },
|
||
|
|
{ id: 'c', label: 'C', x: 45, y: 25, era: '1986' },
|
||
|
|
{ id: 'asm', label: '68K ASM', x: 75, y: 20, era: '1987' },
|
||
|
|
{ id: 'unix', label: 'Unix', x: 30, y: 45, era: '1990' },
|
||
|
|
{ id: 'linux', label: 'Linux', x: 60, y: 50, era: '1994' },
|
||
|
|
{ id: 'python', label: 'Python', x: 25, y: 75, era: '2020' },
|
||
|
|
{ id: 'rust', label: 'Rust', x: 55, y: 80, era: '2022' },
|
||
|
|
{ id: 'ai', label: 'KI', x: 80, y: 70, era: '2023' },
|
||
|
|
]
|
||
|
|
|
||
|
|
const connections = [
|
||
|
|
['fortran', 'c'],
|
||
|
|
['c', 'asm'],
|
||
|
|
['c', 'unix'],
|
||
|
|
['unix', 'linux'],
|
||
|
|
['linux', 'python'],
|
||
|
|
['linux', 'rust'],
|
||
|
|
['python', 'ai'],
|
||
|
|
['rust', 'ai'],
|
||
|
|
]
|
||
|
|
|
||
|
|
export function SystemDiagram() {
|
||
|
|
const getNodePosition = (id: string) => {
|
||
|
|
const node = nodes.find(n => n.id === id)
|
||
|
|
return node ? { x: node.x, y: node.y } : { x: 0, y: 0 }
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="relative aspect-square w-full max-w-md mx-auto">
|
||
|
|
{/* SVG for connections */}
|
||
|
|
<svg className="absolute inset-0 w-full h-full" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
||
|
|
<defs>
|
||
|
|
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||
|
|
<stop offset="0%" stopColor="currentColor" stopOpacity="0.1" />
|
||
|
|
<stop offset="50%" stopColor="currentColor" stopOpacity="0.3" />
|
||
|
|
<stop offset="100%" stopColor="currentColor" stopOpacity="0.1" />
|
||
|
|
</linearGradient>
|
||
|
|
</defs>
|
||
|
|
|
||
|
|
{connections.map(([from, to], index) => {
|
||
|
|
const start = getNodePosition(from)
|
||
|
|
const end = getNodePosition(to)
|
||
|
|
return (
|
||
|
|
<motion.line
|
||
|
|
key={`${from}-${to}`}
|
||
|
|
x1={start.x}
|
||
|
|
y1={start.y}
|
||
|
|
x2={end.x}
|
||
|
|
y2={end.y}
|
||
|
|
stroke="url(#lineGradient)"
|
||
|
|
strokeWidth="0.5"
|
||
|
|
className="text-accent"
|
||
|
|
initial={{ pathLength: 0, opacity: 0 }}
|
||
|
|
animate={{ pathLength: 1, opacity: 1 }}
|
||
|
|
transition={{
|
||
|
|
duration: 0.8,
|
||
|
|
delay: 0.5 + index * 0.1,
|
||
|
|
ease: 'easeOut'
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
)
|
||
|
|
})}
|
||
|
|
</svg>
|
||
|
|
|
||
|
|
{/* Nodes */}
|
||
|
|
{nodes.map((node, index) => (
|
||
|
|
<motion.div
|
||
|
|
key={node.id}
|
||
|
|
className="absolute transform -translate-x-1/2 -translate-y-1/2"
|
||
|
|
style={{ left: `${node.x}%`, top: `${node.y}%` }}
|
||
|
|
initial={{ opacity: 0, scale: 0 }}
|
||
|
|
animate={{ opacity: 1, scale: 1 }}
|
||
|
|
transition={{
|
||
|
|
duration: 0.4,
|
||
|
|
delay: 0.3 + index * 0.08,
|
||
|
|
type: 'spring',
|
||
|
|
stiffness: 200
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<div className="group relative">
|
||
|
|
{/* Node circle */}
|
||
|
|
<div className="relative">
|
||
|
|
<div className="size-3 rounded-full bg-accent/20 border border-accent/40 group-hover:bg-accent/30 transition-colors" />
|
||
|
|
<div className="absolute inset-0 size-3 rounded-full bg-accent/60 animate-ping opacity-0 group-hover:opacity-30" />
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Label */}
|
||
|
|
<div className="absolute top-full left-1/2 -translate-x-1/2 mt-1.5 whitespace-nowrap">
|
||
|
|
<span className="font-mono text-[10px] text-muted-foreground group-hover:text-foreground transition-colors">
|
||
|
|
{node.label}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{/* Era tooltip */}
|
||
|
|
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1.5 opacity-0 group-hover:opacity-100 transition-opacity">
|
||
|
|
<span className="font-mono text-[9px] text-accent bg-background/80 px-1.5 py-0.5 rounded border border-border/50">
|
||
|
|
{node.era}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</motion.div>
|
||
|
|
))}
|
||
|
|
|
||
|
|
{/* Decorative elements */}
|
||
|
|
<div className="absolute inset-0 pointer-events-none">
|
||
|
|
{/* Corner accents */}
|
||
|
|
<div className="absolute top-0 left-0 w-8 h-8 border-l border-t border-border/30" />
|
||
|
|
<div className="absolute top-0 right-0 w-8 h-8 border-r border-t border-border/30" />
|
||
|
|
<div className="absolute bottom-0 left-0 w-8 h-8 border-l border-b border-border/30" />
|
||
|
|
<div className="absolute bottom-0 right-0 w-8 h-8 border-r border-b border-border/30" />
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
)
|
||
|
|
}
|