Initial commit
This commit is contained in:
41
src/pages/network/About.tsx
Normal file
41
src/pages/network/About.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Button } from '../../components/Button'
|
||||
import { CircleBackground } from '../../components/CircleBackground'
|
||||
import { Container } from '../../components/Container'
|
||||
|
||||
export function About() {
|
||||
return (
|
||||
<section
|
||||
id="about"
|
||||
className="relative overflow-hidden bg-gray-900 py-20 lg:py-32 lg:top-0 top-0"
|
||||
>
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
<CircleBackground color="#06b6d4" className="animate-spin-slower" />
|
||||
</div>
|
||||
<Container className="relative">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<h2 className="text-base/7 font-semibold text-cyan-500">Our Mission</h2>
|
||||
<p className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
|
||||
Discover Mycelium
|
||||
</p>
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Mycelium is an unbreakable network, always finding the shortest path and providing 100% secure, peer-to-peer communication. But this is just the beginning.
|
||||
</p>
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Our mission is to create a sustainable digital ecosystem where communication is seamless, data is secure, and scalability knows no bounds.
|
||||
</p>
|
||||
<div className="mt-8 flex justify-center">
|
||||
<Button
|
||||
to="https://threefold.info/mycelium_network/docs/"
|
||||
as="a"
|
||||
target="_blank"
|
||||
variant="outline"
|
||||
color="white"
|
||||
>
|
||||
Learn More
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
40
src/pages/network/CallToAction.tsx
Normal file
40
src/pages/network/CallToAction.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { CircleBackground } from '../../components/CircleBackground'
|
||||
import { Container } from '../../components/Container'
|
||||
import { Button } from '../../components/Button'
|
||||
|
||||
export function CallToAction() {
|
||||
return (
|
||||
<section
|
||||
id="get-started"
|
||||
className="relative overflow-hidden bg-gray-900 py-20 sm:py-28"
|
||||
>
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
<CircleBackground color="#06b6d4" className="animate-spin-slower" />
|
||||
</div>
|
||||
<Container className="relative">
|
||||
<div className="mx-auto max-w-2xl text-center">
|
||||
<h2 className="text-3xl lg:text-4xl font-medium tracking-tight text-white sm:text-4xl">
|
||||
Get Started Today
|
||||
</h2>
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Download the Mycelium app and step into the future of secure, peer-to-peer networking; fast, private, and decentralized.
|
||||
</p>
|
||||
<div className="mt-10 flex flex-wrap justify-center gap-x-6 gap-y-4">
|
||||
<Button to="/download" variant="solid" color="white">
|
||||
Get Mycelium
|
||||
</Button>
|
||||
<Button
|
||||
to="https://threefold.info/mycelium_network/docs/"
|
||||
as="a"
|
||||
target="_blank"
|
||||
variant="outline"
|
||||
color="white"
|
||||
>
|
||||
Read Docs
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
111
src/pages/network/Features.tsx
Normal file
111
src/pages/network/Features.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import Pathfinding from './animations/Pathfinding'
|
||||
import MessageBus from './animations/MessageBus'
|
||||
import ProxyDetection from './animations/ProxyDetection'
|
||||
import ProxyForwarding from './animations/ProxyForwarding'
|
||||
import ContentDistribution from './animations/ContentDistribution'
|
||||
|
||||
export function Features() {
|
||||
return (
|
||||
<section id="features" className="py-24">
|
||||
<div className="mx-auto max-w-2xl px-6 lg:max-w-7xl lg:px-8">
|
||||
<h2 className="text-base/7 font-semibold text-cyan-500">Core Components</h2>
|
||||
<p className="mt-2 max-w-2xl text-3xl lg:text-4xl font-medium tracking-tight text-pretty text-gray-950">
|
||||
Network Capabilities
|
||||
</p>
|
||||
<p className="mt-4 max-w-4xl text-lg text-gray-600">
|
||||
Built for resilience and autonomy, the Mycelium Network dynamically connects nodes through intelligent routing, proxy discovery, and decentralized delivery.
|
||||
</p>
|
||||
<p className="mt-2 max-w-4xl text-lg text-gray-600">
|
||||
Each component — from message passing to content distribution — works in harmony to create a fully self-healing, self-optimizing data mesh.
|
||||
</p>
|
||||
<div className="mt-10 grid grid-cols-1 gap-x-4 gap-y-8 sm:mt-16 lg:grid-cols-6 lg:grid-rows-2">
|
||||
<div className="group relative lg:col-span-3 transition-all duration-300 ease-in-out hover:scale-105">
|
||||
<div className="absolute inset-0 rounded-lg bg-white max-lg:rounded-t-4xl lg:rounded-tl-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-t-[calc(2rem+1px)] lg:rounded-tl-[calc(2rem+1px)]">
|
||||
<Pathfinding />
|
||||
<div className="p-10 pt-4">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Routing</h3>
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950">
|
||||
Automatic pathfinding
|
||||
</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
The Mycelium Network automatically discovers the shortest and fastest routes between nodes,
|
||||
ensuring optimal data flow and network efficiency without manual configuration.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-t-4xl lg:rounded-tl-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
|
||||
</div>
|
||||
<div className="group relative lg:col-span-3 transition-all duration-300 ease-in-out hover:scale-105">
|
||||
<div className="absolute inset-0 rounded-lg bg-white lg:rounded-tr-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-tr-[calc(2rem+1px)]">
|
||||
<MessageBus />
|
||||
<div className="p-10 pt-4">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Communication</h3>
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950">
|
||||
Distributed message bus
|
||||
</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
Acts as a global message layer that lets nodes exchange information seamlessly.
|
||||
Enables resilient, asynchronous communication across the entire decentralized mesh.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 lg:rounded-tr-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
|
||||
</div>
|
||||
<div className="group relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105">
|
||||
<div className="absolute inset-0 rounded-lg bg-white lg:rounded-bl-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] lg:rounded-bl-[calc(2rem+1px)]">
|
||||
<ProxyDetection className="h-80" />
|
||||
<div className="p-10 pt-4">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Discovery</h3>
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950">
|
||||
Automatic proxy detection
|
||||
</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
The system continuously scans for open SOCKS5 proxies within the network,
|
||||
making it effortless to find available connection points without manual setup.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 lg:rounded-bl-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
|
||||
</div>
|
||||
<div className="group relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105">
|
||||
<div className="absolute inset-0 rounded-lg bg-white" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)]">
|
||||
<ProxyForwarding className="h-80" />
|
||||
<div className="p-10 pt-4">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Connectivity</h3>
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950">
|
||||
Seamless proxy forwarding
|
||||
</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
Local SOCKS5 connections can be forwarded through nearby nodes or remote proxies.
|
||||
When browsers use the local proxy, traffic moves securely through the mesh—like a built-in VPN.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
|
||||
</div>
|
||||
<div className="group relative lg:col-span-2 transition-all duration-300 ease-in-out hover:scale-105">
|
||||
<div className="absolute inset-0 rounded-lg bg-white max-lg:rounded-b-4xl lg:rounded-br-4xl" />
|
||||
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(var(--radius-lg)+1px)] max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-br-[calc(2rem+1px)]">
|
||||
<ContentDistribution className="h-80" />
|
||||
<div className="p-10 pt-4">
|
||||
<h3 className="text-sm/4 font-semibold text-cyan-500">Delivery</h3>
|
||||
<p className="mt-2 text-lg font-medium tracking-tight text-gray-950">
|
||||
Decentralized content distribution
|
||||
</p>
|
||||
<p className="mt-2 max-w-lg text-sm/6 text-gray-600">
|
||||
Mycelium can serve data from distributed 0-DBs, creating a CDN-like layer that delivers
|
||||
content faster and more reliably—without relying on centralized servers.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pointer-events-none absolute inset-0 rounded-lg shadow-sm outline outline-black/5 max-lg:rounded-b-4xl lg:rounded-br-4xl group-hover:outline-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
114
src/pages/network/Hero.tsx
Normal file
114
src/pages/network/Hero.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { useId } from 'react'
|
||||
import { Container } from '../../components/Container'
|
||||
import { Button } from '../../components/Button'
|
||||
|
||||
function BackgroundIllustration(props: React.ComponentPropsWithoutRef<'div'>) {
|
||||
let id = useId()
|
||||
|
||||
return (
|
||||
<div {...props}>
|
||||
<svg
|
||||
viewBox="0 0 1026 1026"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
className="absolute inset-0 h-full w-full animate-spin-slow"
|
||||
>
|
||||
<path
|
||||
d="M1025 513c0 282.77-229.23 512-512 512S1 795.77 1 513 230.23 1 513 1s512 229.23 512 512Z"
|
||||
stroke="#D4D4D4"
|
||||
strokeOpacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M513 1025C230.23 1025 1 795.77 1 513"
|
||||
stroke={`url(#${id}-gradient-1)`}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id={`${id}-gradient-1`}
|
||||
x1="1"
|
||||
y1="513"
|
||||
x2="1"
|
||||
y2="1025"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#06b6d4" />
|
||||
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<svg
|
||||
viewBox="0 0 1026 1026"
|
||||
fill="none"
|
||||
aria-hidden="true"
|
||||
className="absolute inset-0 h-full w-full animate-spin-reverse-slower"
|
||||
>
|
||||
<path
|
||||
d="M913 513c0 220.914-179.086 400-400 400S113 733.914 113 513s179.086-400 400-400 400 179.086 400 400Z"
|
||||
stroke="#D4D4D4"
|
||||
strokeOpacity="0.7"
|
||||
/>
|
||||
<path
|
||||
d="M913 513c0 220.914-179.086 400-400 400"
|
||||
stroke={`url(#${id}-gradient-2)`}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id={`${id}-gradient-2`}
|
||||
x1="913"
|
||||
y1="513"
|
||||
x2="913"
|
||||
y2="913"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#06b6d4" />
|
||||
<stop offset="1" stopColor="#06b6d4" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function Hero() {
|
||||
return (
|
||||
<div className="overflow-hidden lg:py-32 lg:pb-0 pb-24">
|
||||
<Container>
|
||||
<div className="flex flex-col-reverse gap-y-16 lg:grid lg:grid-cols-12 lg:gap-x-8 lg:gap-y-20">
|
||||
<div className="relative z-10 mx-auto max-w-2xl lg:col-span-7 lg:max-w-none lg:pt-6 xl:col-span-6">
|
||||
<h1 className="text-4xl lg:text-6xl font-medium tracking-tight text-gray-900">
|
||||
Mycelium
|
||||
</h1>
|
||||
<h2 className="mt-6 lg:text-2xl text-xl tracking-tight leading-normal text-gray-600">
|
||||
Unleashing the Power of Decentralized Networks
|
||||
</h2>
|
||||
<p className="mt-6 lg:text-xl text-lg text-gray-600 lg:leading-normal leading-tight">
|
||||
Discover Mycelium, an end-to-end encrypted IPv6 overlay network. The future of secure, efficient, and scalable networking.
|
||||
</p>
|
||||
<p className="mt-6 text-lg text-gray-600">
|
||||
Coming Soon: New Decentralized Features
|
||||
</p>
|
||||
<div className="mt-8 flex flex-wrap gap-x-6 gap-y-4">
|
||||
<Button to="/download" variant="solid" color="cyan">
|
||||
Get Mycelium
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative lg:mt-10 mt-0 lg:col-span-5 lg:row-span-2 xl:col-span-6">
|
||||
<BackgroundIllustration className="absolute top-4 left-1/2 h-[1026px] w-[1026px] -translate-x-1/2 stroke-gray-300/70 sm:top-16 lg:-top-12 lg:ml-12 ml-0" />
|
||||
<div className="mx-auto h-[448px] mask-[linear-gradient(to_bottom,white_60%,transparent)] lg:px-0 lg:absolute lg:-inset-x-10 lg:-top-24 lg:h-auto lg:pt-10 xl:-bottom-32">
|
||||
<img
|
||||
src="/src/images/phoneframe.png"
|
||||
alt="Mycelium application demo"
|
||||
className="mx-auto max-w-[366px]"
|
||||
width={366}
|
||||
height={729}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
32
src/pages/network/NetworkPage.tsx
Normal file
32
src/pages/network/NetworkPage.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { AnimatedSection } from '../../components/AnimatedSection'
|
||||
import { Hero } from './Hero'
|
||||
import { About } from './About'
|
||||
import { Features } from './Features'
|
||||
import { PrimaryFeatures } from './PrimaryFeatures'
|
||||
import { SecondaryFeatures } from './SecondaryFeatures'
|
||||
import { CallToAction } from './CallToAction'
|
||||
|
||||
export default function NetworkPage() {
|
||||
return (
|
||||
<>
|
||||
<AnimatedSection>
|
||||
<Hero />
|
||||
</AnimatedSection>
|
||||
<AnimatedSection>
|
||||
<About />
|
||||
</AnimatedSection>
|
||||
<AnimatedSection>
|
||||
<Features />
|
||||
</AnimatedSection>
|
||||
<AnimatedSection>
|
||||
<PrimaryFeatures />
|
||||
</AnimatedSection>
|
||||
<AnimatedSection>
|
||||
<SecondaryFeatures />
|
||||
</AnimatedSection>
|
||||
<AnimatedSection>
|
||||
<CallToAction />
|
||||
</AnimatedSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
28
src/pages/network/PrimaryFeatures.tsx
Normal file
28
src/pages/network/PrimaryFeatures.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Container } from '../../components/Container'
|
||||
|
||||
export function PrimaryFeatures() {
|
||||
return (
|
||||
<section
|
||||
id="howitworks"
|
||||
aria-label="How Mycelium works"
|
||||
className="bg-gray-900 py-20 sm:py-32"
|
||||
>
|
||||
<Container>
|
||||
<div className="mx-auto max-w-2xl lg:mx-0 lg:max-w-3xl">
|
||||
<h2 className="text-base/7 font-semibold text-cyan-500">How It Works</h2>
|
||||
<p className="text-3xl lg:text-4xl font-medium tracking-tight text-white">
|
||||
How Mycelium Operates
|
||||
</p>
|
||||
<p className="mt-6 text-lg text-gray-300">
|
||||
Mycelium, like its natural namesake, thrives on decentralization, efficiency, and security, making it a truly powerful force in the world of decentralized networks.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-16 text-center">
|
||||
<p className="text-lg text-gray-400">
|
||||
Interactive features demonstration coming soon...
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
225
src/pages/network/SecondaryFeatures.tsx
Normal file
225
src/pages/network/SecondaryFeatures.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
import { useId } from 'react'
|
||||
import { Container } from '../../components/Container'
|
||||
|
||||
const features = [
|
||||
{
|
||||
name: 'Quantum Safe Storage Functionality',
|
||||
description:
|
||||
"Mycelium's quantum safe storage enables flexible, scalable, and efficient data distribution across a decentralized network, ensuring redundancy and security.",
|
||||
icon: DeviceArrowIcon,
|
||||
},
|
||||
{
|
||||
name: 'Entry and Exit Points for AI Workloads',
|
||||
description:
|
||||
'Seamlessly connect AI applications to Mycelium, providing optimized and secured data pipelines for training, inference, and real-time processing.',
|
||||
icon: DeviceCardsIcon,
|
||||
},
|
||||
{
|
||||
name: 'Data Storage and Retrieval Mechanisms',
|
||||
description:
|
||||
'Users can choose between storing data locally for quick access or utilizing the distributed grid for enhanced scalability and resilience.',
|
||||
icon: DeviceClockIcon,
|
||||
},
|
||||
{
|
||||
name: 'Integrated Name Services (DNS)',
|
||||
description:
|
||||
'The Integrated DNS system efficiently finds the shortest path between users and websites, automatically balancing loads and identifying alternative routes in case of internet issues.',
|
||||
icon: DeviceListIcon,
|
||||
},
|
||||
{
|
||||
name: 'Frontend/Backend Integration',
|
||||
description:
|
||||
'Mycelium provides seamless integration with existing applications, enabling developers to leverage decentralized storage across both frontend and backend architectures.',
|
||||
icon: DeviceLockIcon,
|
||||
},
|
||||
{
|
||||
name: 'CDN (Content Delivery Network)',
|
||||
description:
|
||||
'Mycelium accelerates data distribution by acting as a decentralized CDN, ensuring fast, secure, and efficient content delivery across global nodes with minimal latency.',
|
||||
icon: DeviceChartIcon,
|
||||
},
|
||||
]
|
||||
|
||||
function DeviceArrowIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9 0a4 4 0 00-4 4v24a4 4 0 004 4h14a4 4 0 004-4V4a4 4 0 00-4-4H9zm0 2a2 2 0 00-2 2v24a2 2 0 002 2h14a2 2 0 002-2V4a2 2 0 00-2-2h-1.382a1 1 0 00-.894.553l-.448.894a1 1 0 01-.894.553h-6.764a1 1 0 01-.894-.553l-.448-.894A1 1 0 0010.382 2H9z"
|
||||
fill="#737373"
|
||||
/>
|
||||
<path
|
||||
d="M12 25l8-8m0 0h-6m6 0v6"
|
||||
stroke="#171717"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
<circle cx={16} cy={16} r={16} fill="#A3A3A3" fillOpacity={0.2} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function DeviceCardsIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
let id = useId()
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9 0a4 4 0 00-4 4v24a4 4 0 004 4h14a4 4 0 004-4V4a4 4 0 00-4-4H9zm0 2a2 2 0 00-2 2v24a2 2 0 002 2h14a2 2 0 002-2V4a2 2 0 00-2-2h-1.382a1 1 0 00-.894.553l-.448.894a1 1 0 01-.894.553h-6.764a1 1 0 01-.894-.553l-.448-.894A1 1 0 0010.382 2H9z"
|
||||
fill="#737373"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9 13a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H10a1 1 0 01-1-1v-2zm0 6a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H10a1 1 0 01-1-1v-2zm1 5a1 1 0 00-1 1v2a1 1 0 001 1h12a1 1 0 001-1v-2a1 1 0 00-1-1H10z"
|
||||
fill={`url(#${id}-gradient)`}
|
||||
/>
|
||||
<rect x={9} y={6} width={14} height={4} rx={1} fill="#171717" />
|
||||
<circle cx={16} cy={16} r={16} fill="#A3A3A3" fillOpacity={0.2} />
|
||||
<defs>
|
||||
<linearGradient
|
||||
id={`${id}-gradient`}
|
||||
x1={16}
|
||||
y1={12}
|
||||
x2={16}
|
||||
y2={28}
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#737373" />
|
||||
<stop offset={1} stopColor="#737373" stopOpacity={0} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function DeviceClockIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" aria-hidden="true" {...props}>
|
||||
<circle cx={16} cy={16} r={16} fill="#A3A3A3" fillOpacity={0.2} />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M5 4a4 4 0 014-4h14a4 4 0 014 4v10h-2V4a2 2 0 00-2-2h-1.382a1 1 0 00-.894.553l-.448.894a1 1 0 01-.894.553h-6.764a1 1 0 01-.894-.553l-.448-.894A1 1 0 0010.382 2H9a2 2 0 00-2 2v24a2 2 0 002 2h5v2H9a4 4 0 01-4-4V4z"
|
||||
fill="#737373"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M24 32a8 8 0 100-16 8 8 0 000 16zm1-8.414V19h-2v5.414l4 4L28.414 27 25 23.586z"
|
||||
fill="#171717"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function DeviceListIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" fill="none" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9 0a4 4 0 00-4 4v24a4 4 0 004 4h14a4 4 0 004-4V4a4 4 0 00-4-4H9zm0 2a2 2 0 00-2 2v24a2 2 0 002 2h14a2 2 0 002-2V4a2 2 0 00-2-2h-1.382a1 1 0 00-.894.553l-.448.894a1 1 0 01-.894.553h-6.764a1 1 0 01-.894-.553l-.448-.894A1 1 0 0010.382 2H9z"
|
||||
fill="#737373"
|
||||
/>
|
||||
<circle cx={11} cy={14} r={2} fill="#171717" />
|
||||
<circle cx={11} cy={20} r={2} fill="#171717" />
|
||||
<circle cx={11} cy={26} r={2} fill="#171717" />
|
||||
<path
|
||||
d="M16 14h6M16 20h6M16 26h6"
|
||||
stroke="#737373"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
<circle cx={16} cy={16} r={16} fill="#A3A3A3" fillOpacity={0.2} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function DeviceLockIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" aria-hidden="true" {...props}>
|
||||
<circle cx={16} cy={16} r={16} fill="#A3A3A3" fillOpacity={0.2} />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M5 4a4 4 0 014-4h14a4 4 0 014 4v10h-2V4a2 2 0 00-2-2h-1.382a1 1 0 00-.894.553l-.448.894a1 1 0 01-.894.553h-6.764a1 1 0 01-.894-.553l-.448-.894A1 1 0 0010.382 2H9a2 2 0 00-2 2v24a2 2 0 002 2h5v2H9a4 4 0 01-4-4V4z"
|
||||
fill="#737373"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M18 19.5a3.5 3.5 0 117 0V22a2 2 0 012 2v6a2 2 0 01-2 2h-7a2 2 0 01-2-2v-6a2 2 0 012-2v-2.5zm2 2.5h3v-2.5a1.5 1.5 0 00-3 0V22z"
|
||||
fill="#171717"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function DeviceChartIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 32 32" fill="none" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M9 0a4 4 0 00-4 4v24a4 4 0 004 4h14a4 4 0 004-4V4a4 4 0 00-4-4H9zm0 2a2 2 0 00-2 2v24a2 2 0 002 2h14a2 2 0 002-2V4a2 2 0 00-2-2h-1.382a1 1 0 00-.894.553l-.448.894a1 1 0 01-.894.553h-6.764a1 1 0 01-.894-.553l-.448-.894A1 1 0 0010.382 2H9z"
|
||||
fill="#737373"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M23 13.838V26a2 2 0 01-2 2H11a2 2 0 01-2-2V15.65l2.57 3.212a1 1 0 001.38.175L15.4 17.2a1 1 0 011.494.353l1.841 3.681c.399.797 1.562.714 1.843-.13L23 13.837z"
|
||||
fill="#171717"
|
||||
/>
|
||||
<path
|
||||
d="M10 12h12"
|
||||
stroke="#737373"
|
||||
strokeWidth={2}
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
<circle cx={16} cy={16} r={16} fill="#A3A3A3" fillOpacity={0.2} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SecondaryFeatures() {
|
||||
return (
|
||||
<section
|
||||
id="comingsoon"
|
||||
aria-label="Features for building a portfolio"
|
||||
className="py-20 sm:py-32"
|
||||
>
|
||||
<Container>
|
||||
<div className="mx-auto max-w-4xl sm:text-center">
|
||||
<h2 className="text-base/7 font-semibold text-cyan-500">Roadmap</h2>
|
||||
<p className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900">
|
||||
Coming Soon: The Future of Mycelium
|
||||
</p>
|
||||
<p className="mt-6 text-lg text-gray-600">
|
||||
Mycelium is evolving to bring even more powerful decentralized features, designed to enhance your experience and expand possibilities. Be the first to explore what's coming next by staying connected with our latest updates.
|
||||
</p>
|
||||
</div>
|
||||
<ul
|
||||
role="list"
|
||||
className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-6 text-sm sm:mt-20 sm:grid-cols-2 md:gap-y-10 lg:max-w-none lg:grid-cols-3"
|
||||
>
|
||||
{features.map((feature) => (
|
||||
<li
|
||||
key={feature.name}
|
||||
className="rounded-2xl border border-gray-200 p-8 transition-all duration-300 ease-in-out hover:scale-105 hover:border-cyan-500 hover:shadow-lg hover:shadow-cyan-500/20"
|
||||
>
|
||||
<feature.icon className="h-8 w-8" />
|
||||
<h3 className="mt-6 font-semibold text-gray-900">
|
||||
{feature.name}
|
||||
</h3>
|
||||
<p className="mt-2 text-gray-700">{feature.description}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Container>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
161
src/pages/network/animations/ContentDistribution.tsx
Normal file
161
src/pages/network/animations/ContentDistribution.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const ACCENT = '#00b8db';
|
||||
const STROKE = '#111827';
|
||||
const GRAY = '#9CA3AF';
|
||||
const GRAY_LT = '#E5E7EB';
|
||||
|
||||
const IconSquare = () => (
|
||||
<rect x={-14} y={-14} width={28} height={28} rx={6} fill={ACCENT} stroke={STROKE} strokeWidth={3} />
|
||||
);
|
||||
const IconTriangle = () => (
|
||||
<path d="M 0 -15 L 14 12 L -14 12 Z" fill="#fff" stroke={STROKE} strokeWidth={3} />
|
||||
);
|
||||
const IconHex = () => (
|
||||
<path
|
||||
d="M 0 -15 L 13 -7 L 13 7 L 0 15 L -13 7 L -13 -7 Z"
|
||||
fill="#fff"
|
||||
stroke={STROKE}
|
||||
strokeWidth={3}
|
||||
/>
|
||||
);
|
||||
const IconBolt = () => (
|
||||
<path d="M -5 -14 L 4 -2 L -1 -2 L 5 14 L -6 1 L -1 1 Z" fill={ACCENT} stroke={STROKE} strokeWidth={3} />
|
||||
);
|
||||
const IconPlay = () => (
|
||||
<circle r={15} fill="#fff" stroke={STROKE} strokeWidth={3} />
|
||||
);
|
||||
const IconDB = () => (
|
||||
<>
|
||||
<ellipse cx={0} cy={-10} rx={16} ry={8} fill="#fff" stroke={STROKE} strokeWidth={3} />
|
||||
<rect x={-16} y={-10} width={32} height={20} fill="#fff" stroke={STROKE} strokeWidth={3} />
|
||||
<ellipse cx={0} cy={10} rx={16} ry={8} fill="#fff" stroke={STROKE} strokeWidth={3} />
|
||||
</>
|
||||
);
|
||||
|
||||
function Cloud({ pulse = true }: { pulse?: boolean }) {
|
||||
const prefersReduced = useReducedMotion();
|
||||
return (
|
||||
<g>
|
||||
<g fill={STROKE}>
|
||||
<circle cx={-18} cy={0} r={14} />
|
||||
<circle cx={0} cy={-10} r={18} />
|
||||
<circle cx={18} cy={0} r={16} />
|
||||
<rect x={-30} y={0} width={54} height={16} rx={8} />
|
||||
</g>
|
||||
<motion.circle
|
||||
r={36}
|
||||
fill="none"
|
||||
stroke={ACCENT}
|
||||
strokeWidth={4}
|
||||
initial={{ opacity: 0.15, scale: 0.9 }}
|
||||
animate={pulse && !prefersReduced ? { opacity: [0.15, 0.35, 0.15], scale: [0.9, 1.05, 0.9] } : {}}
|
||||
transition={{ duration: 1.8, repeat: Infinity }}
|
||||
/>
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
function Beam({
|
||||
x2,
|
||||
y2,
|
||||
delay = 0,
|
||||
}: {
|
||||
x2: number;
|
||||
y2: number;
|
||||
delay?: number;
|
||||
}) {
|
||||
const prefersReduced = useReducedMotion();
|
||||
return (
|
||||
<motion.line
|
||||
x1={0}
|
||||
y1={0}
|
||||
x2={x2}
|
||||
y2={y2}
|
||||
stroke={ACCENT}
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.0 }}
|
||||
animate={{ pathLength: 1, opacity: 0.9 }}
|
||||
transition={{
|
||||
duration: prefersReduced ? 0.01 : 0.9,
|
||||
delay,
|
||||
repeat: prefersReduced ? 0 : Infinity,
|
||||
repeatDelay: 1.2,
|
||||
repeatType: 'reverse',
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ContentDistribution({ className, bg = '#ffffff' }: Props) {
|
||||
const W = 900;
|
||||
const H = 560;
|
||||
|
||||
const rings = [110, 190, 270];
|
||||
|
||||
const layout = [
|
||||
{ r: rings[1], a: -20, icon: <IconSquare /> },
|
||||
{ r: rings[2], a: 20, icon: <IconTriangle /> },
|
||||
{ r: rings[0], a: 155, icon: <IconHex /> },
|
||||
{ r: rings[2], a: -145, icon: <IconBolt /> },
|
||||
{ r: rings[1], a: 210, icon: <IconDB /> },
|
||||
{ r: rings[0], a: 60, icon: <IconPlay /> },
|
||||
];
|
||||
|
||||
const prefersReduced = useReducedMotion();
|
||||
|
||||
return (
|
||||
<div className={className} aria-hidden="true" role="img" style={{ background: bg }}>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} width="100%" height="100%">
|
||||
<defs>
|
||||
<radialGradient id="fade" cx="50%" cy="50%" r="60%">
|
||||
<stop offset="0%" stopColor="#ffffff" />
|
||||
<stop offset="100%" stopColor="#ffffff" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<rect width={W} height={H} fill="url(#fade)" />
|
||||
<g transform={`translate(${W / 2}, ${H / 2})`}>
|
||||
{rings.map((r, i) => (
|
||||
<circle key={i} r={r} fill="none" stroke={GRAY_LT} strokeWidth={2} />
|
||||
))}
|
||||
|
||||
<Cloud />
|
||||
|
||||
<motion.g
|
||||
initial={{ rotate: 0 }}
|
||||
animate={{ rotate: prefersReduced ? 0 : 360 }}
|
||||
transition={{ duration: 40, ease: 'linear', repeat: prefersReduced ? 0 : Infinity }}
|
||||
>
|
||||
{layout.map((n, i) => {
|
||||
const rad = (n.a * Math.PI) / 180;
|
||||
const x = n.r * Math.cos(rad);
|
||||
const y = n.r * Math.sin(rad);
|
||||
return <Beam key={`beam-${i}`} x2={x} y2={y} delay={i * 0.15} />;
|
||||
})}
|
||||
|
||||
{layout.map((n, i) => {
|
||||
const rad = (n.a * Math.PI) / 180;
|
||||
const x = n.r * Math.cos(rad);
|
||||
const y = n.r * Math.sin(rad);
|
||||
return (
|
||||
<g key={`badge-${i}`} transform={`translate(${x}, ${y})`}>
|
||||
<circle r={34} fill="#fff" stroke={GRAY_LT} strokeWidth={3} />
|
||||
<g transform="scale(1)">
|
||||
{n.icon}
|
||||
</g>
|
||||
</g>
|
||||
);
|
||||
})}
|
||||
</motion.g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
132
src/pages/network/animations/MessageBus.tsx
Normal file
132
src/pages/network/animations/MessageBus.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const ACCENT = '#00b8db';
|
||||
const STROKE = '#111827';
|
||||
const GRAY = '#9CA3AF';
|
||||
const GRAY_LT = '#E5E7EB';
|
||||
|
||||
function Envelope({
|
||||
x, y, w = 88, h = 56, fill = GRAY_LT, accent = false, delay = 0, duration = 1.6,
|
||||
path = 'none',
|
||||
reverse = false,
|
||||
}: {
|
||||
x: number; y: number; w?: number; h?: number; fill?: string; accent?: boolean;
|
||||
delay?: number; duration?: number; path?: 'left1'|'left2'|'rightTop'|'rightBottom'|'none'; reverse?: boolean;
|
||||
}) {
|
||||
const prefersReduced = useReducedMotion();
|
||||
|
||||
const paths: Record<string, { x: number[]; y: number[] }> = {
|
||||
left1: { x: [x, 380], y: [y, 220] },
|
||||
left2: { x: [x, 380], y: [y, 220] },
|
||||
rightTop: { x: [380, 720], y: [220, 150] },
|
||||
rightBottom: { x: [380, 720], y: [220, 290] },
|
||||
none: { x: [x], y: [y] },
|
||||
};
|
||||
|
||||
const k = paths[path];
|
||||
|
||||
return (
|
||||
<motion.g
|
||||
initial={{ opacity: 0, scale: 0.98 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
x: prefersReduced ? 0 : (reverse ? [...k.x].reverse() : k.x),
|
||||
y: prefersReduced ? 0 : (reverse ? [...k.y].reverse() : k.y),
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: prefersReduced ? 0.01 : duration,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
repeat: prefersReduced ? 0 : Infinity,
|
||||
repeatDelay: 0.6,
|
||||
}}
|
||||
>
|
||||
<rect x={-w / 2} y={-h / 2} width={w} height={h} rx={8} fill={fill} stroke={STROKE} strokeWidth={3} />
|
||||
<path
|
||||
d={`M ${-w/2+4} ${-h/2+6} L 0 ${-h/2+26} L ${w/2-4} ${-h/2+6}`}
|
||||
fill="none"
|
||||
stroke={accent ? ACCENT : STROKE}
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</motion.g>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MessageBus({ className, bg = '#ffffff' }: Props) {
|
||||
const W = 900;
|
||||
const H = 460;
|
||||
|
||||
return (
|
||||
<div className={className} aria-hidden="true" role="img" style={{ background: bg }}>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} width="100%" height="100%">
|
||||
<defs>
|
||||
<pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse">
|
||||
<path d="M 24 0 L 0 0 0 24" fill="none" stroke={GRAY_LT} strokeWidth="1" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width={W} height={H} fill="url(#grid)" />
|
||||
|
||||
{[{cx:140,cy:120},{cx:140,cy:340}].map((n,i)=>(
|
||||
<g key={i}>
|
||||
<circle cx={n.cx} cy={n.cy} r={44} fill="#fff" stroke={STROKE} strokeWidth={4}/>
|
||||
<motion.path
|
||||
d={`M ${n.cx+48} ${n.cy} L 320 ${n.cy>200?260:180}`}
|
||||
fill="none" stroke={STROKE} strokeWidth={4} strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.3 }}
|
||||
animate={{ pathLength: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.9, delay: 0.1 + i*0.1, ease: [0.22,1,0.36,1] }}
|
||||
/>
|
||||
</g>
|
||||
))}
|
||||
|
||||
{[{cx:760,cy:120},{cx:760,cy:340}].map((n,i)=>(
|
||||
<g key={i}>
|
||||
<circle cx={n.cx} cy={n.cy} r={44} fill="#fff" stroke={STROKE} strokeWidth={4}/>
|
||||
<motion.path
|
||||
d={`M 560 ${i===0?180:260} L ${n.cx-48} ${n.cy}`}
|
||||
fill="none" stroke={STROKE} strokeWidth={4} strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.3 }}
|
||||
animate={{ pathLength: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.9, delay: 0.2 + i*0.1, ease: [0.22,1,0.36,1] }}
|
||||
/>
|
||||
</g>
|
||||
))}
|
||||
|
||||
<rect x={330} y={150} width={240} height={140} rx={24} fill="#fff" stroke={STROKE} strokeWidth={4} />
|
||||
{[0,1,2].map(i=>(
|
||||
<rect key={i} x={350 + i*76} y={170} width={64} height={100} rx={12} fill="none" stroke={GRAY} strokeWidth={3}/>
|
||||
))}
|
||||
|
||||
<Envelope x={200} y={120} accent fill="#fff" path="left1" delay={0.0} duration={2.0}/>
|
||||
<Envelope x={200} y={340} fill={GRAY_LT} path="left2" delay={0.4} duration={2.2}/>
|
||||
<Envelope x={200} y={340} accent fill="#fff" path="left2" delay={0.9} duration={2.0}/>
|
||||
|
||||
{[0,1,2].map((i)=>(
|
||||
<motion.g key={i} transform={`translate(${382 + i*76} 220)`}>
|
||||
<motion.rect
|
||||
x={-28} y={-18} width={56} height={36} rx={8}
|
||||
fill={i===2 ? ACCENT : GRAY_LT}
|
||||
stroke={STROKE} strokeWidth={3}
|
||||
initial={{ opacity: 0.6 }}
|
||||
animate={{ opacity: [0.6, 1, 0.6] }}
|
||||
transition={{ duration: 1.8, repeat: Infinity, delay: i*0.2 }}
|
||||
/>
|
||||
<path d="M -24 -12 L 0 0 L 24 -12" fill="none" stroke={STROKE} strokeWidth={4} strokeLinecap="round" />
|
||||
</motion.g>
|
||||
))}
|
||||
|
||||
<Envelope x={560} y={180} accent fill="#fff" path="rightTop" delay={0.6} duration={2.1}/>
|
||||
<Envelope x={560} y={260} fill={GRAY_LT} path="rightBottom" delay={1.0} duration={2.3}/>
|
||||
<Envelope x={560} y={260} accent fill="#fff" path="rightBottom" delay={1.5} duration={2.0}/>
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
209
src/pages/network/animations/Pathfinding.tsx
Normal file
209
src/pages/network/animations/Pathfinding.tsx
Normal file
@@ -0,0 +1,209 @@
|
||||
// Animated SVG illustrating "Automatic pathfinding"
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
import clsx from 'clsx';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
accent?: string;
|
||||
stroke?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const Node = ({
|
||||
cx,
|
||||
cy,
|
||||
r = 16,
|
||||
fill = "#00b8db",
|
||||
ring = "#E5E7EB",
|
||||
pulse = false,
|
||||
rMotion = 2,
|
||||
}: {
|
||||
cx: number;
|
||||
cy: number;
|
||||
r?: number;
|
||||
fill?: string;
|
||||
ring?: string;
|
||||
pulse?: boolean;
|
||||
rMotion?: number;
|
||||
}) => {
|
||||
const prefersReduced = useReducedMotion();
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.circle
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
r={r + 14}
|
||||
fill="none"
|
||||
stroke={ring}
|
||||
strokeWidth={2}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
/>
|
||||
<motion.circle
|
||||
cx={cx}
|
||||
cy={cy}
|
||||
r={r}
|
||||
fill={fill}
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
scale: pulse && !prefersReduced ? [1, 1 + rMotion / 16, 1] : 1,
|
||||
}}
|
||||
transition={{
|
||||
duration: pulse && !prefersReduced ? 1.8 : 0.6,
|
||||
repeat: pulse && !prefersReduced ? Infinity : 0,
|
||||
repeatType: "loop",
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Arrow = ({
|
||||
d,
|
||||
color = "#111827",
|
||||
delay = 0,
|
||||
}: {
|
||||
d: string;
|
||||
color?: string;
|
||||
delay?: number;
|
||||
}) => (
|
||||
<motion.path
|
||||
d={d}
|
||||
fill="none"
|
||||
stroke={color}
|
||||
strokeWidth={3}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
initial={{ pathLength: 0, opacity: 0 }}
|
||||
animate={{ pathLength: 1, opacity: 1 }}
|
||||
transition={{
|
||||
delay,
|
||||
duration: 0.8,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const DashedPath = ({
|
||||
d,
|
||||
color = "#9CA3AF",
|
||||
dash = 6,
|
||||
delay = 0,
|
||||
loop = false,
|
||||
}: {
|
||||
d: string;
|
||||
color?: string;
|
||||
dash?: number;
|
||||
delay?: number;
|
||||
loop?: boolean;
|
||||
}) => {
|
||||
const prefersReduced = useReducedMotion();
|
||||
|
||||
return (
|
||||
<motion.path
|
||||
d={d}
|
||||
fill="none"
|
||||
stroke={color}
|
||||
strokeWidth={3}
|
||||
strokeDasharray={dash}
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.4 }}
|
||||
animate={{
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: 0.9,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
repeat: !prefersReduced && loop ? Infinity : 0,
|
||||
repeatDelay: 1.2,
|
||||
repeatType: "reverse",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Pathfinding({
|
||||
className,
|
||||
accent = "#00b8db",
|
||||
stroke = "#111827",
|
||||
bg = "#FFFFFF",
|
||||
}: Props) {
|
||||
const W = 760;
|
||||
const H = 420;
|
||||
|
||||
const center = { x: 380, y: 210 };
|
||||
const nodes = [
|
||||
{ x: 130, y: 210 },
|
||||
{ x: 670, y: 210 },
|
||||
{ x: 380, y: 70 },
|
||||
{ x: 280, y: 340 },
|
||||
{ x: 500, y: 340 },
|
||||
];
|
||||
|
||||
const arrowTo = (from: { x: number; y: number }, to: { x: number; y: number }) => {
|
||||
const dx = to.x - from.x;
|
||||
const dy = to.y - from.y;
|
||||
const len = Math.hypot(dx, dy);
|
||||
const ux = dx / len;
|
||||
const uy = dy / len;
|
||||
const end = { x: to.x - ux * 18, y: to.y - uy * 18 };
|
||||
const headL = {
|
||||
x: end.x - uy * 8 - ux * 6,
|
||||
y: end.y + ux * 8 - uy * 6,
|
||||
};
|
||||
const headR = {
|
||||
x: end.x + uy * 8 - ux * 6,
|
||||
y: end.y - ux * 8 - uy * 6,
|
||||
};
|
||||
return `M ${from.x} ${from.y} L ${end.x} ${end.y} M ${headL.x} ${headL.y} L ${end.x} ${end.y} L ${headR.x} ${headR.y}`;
|
||||
};
|
||||
|
||||
const highlightA = `M ${nodes[0].x} ${nodes[0].y} L ${center.x} ${center.y}`;
|
||||
const highlightB = `M ${center.x} ${center.y} L ${nodes[4].x} ${nodes[4].y}`;
|
||||
|
||||
const alt1 = `M ${nodes[2].x} ${nodes[2].y} L ${center.x} ${center.y}`;
|
||||
const alt2 = `M ${nodes[3].x} ${nodes[3].y} L ${center.x} ${center.y}`;
|
||||
const alt3 = `M ${center.x} ${center.y} L ${nodes[1].x} ${nodes[1].y}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx("relative overflow-hidden", className)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
aria-label="Automatic pathfinding between nodes"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
||||
<defs>
|
||||
<pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse">
|
||||
<path d="M 24 0 L 0 0 0 24" fill="none" stroke="#F3F4F6" strokeWidth="1" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width={W} height={H} fill="url(#grid)" />
|
||||
|
||||
<DashedPath d={alt1} color="#E5E7EB" dash={5} delay={0.1} />
|
||||
<DashedPath d={alt2} color="#E5E7EB" dash={5} delay={0.2} />
|
||||
<DashedPath d={alt3} color="#E5E7EB" dash={5} delay={0.3} />
|
||||
|
||||
<DashedPath d={highlightA} color={accent} dash={8} delay={0.2} loop />
|
||||
<DashedPath d={highlightB} color={accent} dash={8} delay={0.4} loop />
|
||||
|
||||
<Arrow d={arrowTo(nodes[0], center)} color={stroke} delay={0.1} />
|
||||
<Arrow d={arrowTo(nodes[2], center)} color={stroke} delay={0.2} />
|
||||
<Arrow d={arrowTo(nodes[3], center)} color={stroke} delay={0.25} />
|
||||
<Arrow d={arrowTo(nodes[1], center)} color={stroke} delay={0.3} />
|
||||
|
||||
<Node cx={center.x} cy={center.y} r={18} fill={accent} ring="#E5E7EB" pulse />
|
||||
{nodes.map((n, i) => (
|
||||
<Node key={i} cx={n.x} cy={n.y} r={14} fill="#FFFFFF" ring="#E5E7EB" />
|
||||
))}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
174
src/pages/network/animations/ProxyDetection.tsx
Normal file
174
src/pages/network/animations/ProxyDetection.tsx
Normal file
@@ -0,0 +1,174 @@
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const ACCENT = '#00b8db';
|
||||
const STROKE = '#111827';
|
||||
const GRAY = '#9CA3AF';
|
||||
const GRAY_LT = '#E5E7EB';
|
||||
|
||||
function Magnifier({
|
||||
x = 0,
|
||||
y = 0,
|
||||
flip = false,
|
||||
delay = 0,
|
||||
duration = 3,
|
||||
}: {
|
||||
x?: number;
|
||||
y?: number;
|
||||
flip?: boolean;
|
||||
delay?: number;
|
||||
duration?: number;
|
||||
}) {
|
||||
const prefersReduced = useReducedMotion();
|
||||
|
||||
return (
|
||||
<motion.g
|
||||
initial={{ x: 0 }}
|
||||
animate={{ x: [0, 520] }}
|
||||
transition={{
|
||||
delay,
|
||||
duration: prefersReduced ? 0.01 : duration,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
repeat: prefersReduced ? 0 : Infinity,
|
||||
repeatType: 'reverse',
|
||||
repeatDelay: 0.4,
|
||||
}}
|
||||
transform={`translate(${x}, ${y})`}
|
||||
>
|
||||
<circle cx={0} cy={0} r={38} fill="#fff" stroke={STROKE} strokeWidth={6} />
|
||||
<motion.circle
|
||||
cx={0}
|
||||
cy={0}
|
||||
r={26}
|
||||
fill="none"
|
||||
stroke={ACCENT}
|
||||
strokeWidth={4}
|
||||
initial={{ opacity: 0.15, scale: 0.8 }}
|
||||
animate={{ opacity: [0.15, 0.35, 0.15], scale: [0.8, 1.05, 0.8] }}
|
||||
transition={{ duration: 1.6, repeat: Infinity }}
|
||||
/>
|
||||
<g transform={`rotate(${flip ? 40 : -40}) translate(35, 10)`}>
|
||||
<rect x={0} y={-6} width={80} height={12} rx={6} fill={STROKE} />
|
||||
<rect x={0} y={-12} width={14} height={24} rx={6} fill={GRAY} />
|
||||
</g>
|
||||
</motion.g>
|
||||
);
|
||||
}
|
||||
|
||||
function ServerBox({
|
||||
x,
|
||||
y,
|
||||
w = 88,
|
||||
h = 50,
|
||||
delay = 0,
|
||||
accentPulse = false,
|
||||
}: {
|
||||
x: number;
|
||||
y: number;
|
||||
w?: number;
|
||||
h?: number;
|
||||
delay?: number;
|
||||
accentPulse?: boolean;
|
||||
}) {
|
||||
const prefersReduced = useReducedMotion();
|
||||
|
||||
return (
|
||||
<motion.g
|
||||
transform={`translate(${x}, ${y})`}
|
||||
initial={{ opacity: 0.6 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay, duration: 0.4 }}
|
||||
>
|
||||
<rect
|
||||
x={-w / 2}
|
||||
y={-h / 2}
|
||||
width={w}
|
||||
height={h}
|
||||
rx={10}
|
||||
fill="#fff"
|
||||
stroke={STROKE}
|
||||
strokeWidth={3}
|
||||
/>
|
||||
<rect
|
||||
x={-w / 2 + 6}
|
||||
y={-h / 2 + 8}
|
||||
width={w - 12}
|
||||
height={12}
|
||||
rx={6}
|
||||
fill={GRAY_LT}
|
||||
/>
|
||||
<motion.rect
|
||||
x={-w / 2 + 10}
|
||||
y={-h / 2 + 26}
|
||||
width={w - 20}
|
||||
height={10}
|
||||
rx={5}
|
||||
fill={GRAY_LT}
|
||||
initial={{ width: w * 0.2 }}
|
||||
animate={{ width: [w * 0.2, w - 20, w * 0.2] }}
|
||||
transition={{
|
||||
delay,
|
||||
duration: prefersReduced ? 0.01 : 1.8,
|
||||
repeat: prefersReduced ? 0 : Infinity,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
}}
|
||||
/>
|
||||
<motion.circle
|
||||
cx={w / 2 - 14}
|
||||
cy={h / 2 - 14}
|
||||
r={6}
|
||||
fill={accentPulse ? ACCENT : GRAY}
|
||||
initial={{ scale: 0.9, opacity: 0.8 }}
|
||||
animate={
|
||||
accentPulse && !prefersReduced
|
||||
? { scale: [0.9, 1.15, 0.9], opacity: [0.8, 1, 0.8] }
|
||||
: { scale: 1, opacity: 0.9 }
|
||||
}
|
||||
transition={{ duration: 1.4, repeat: accentPulse && !prefersReduced ? Infinity : 0 }}
|
||||
/>
|
||||
</motion.g>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProxyDetection({ className, bg = '#ffffff' }: Props) {
|
||||
const W = 900;
|
||||
const H = 180;
|
||||
|
||||
const rowY = H / 2;
|
||||
const xs = [180, 320, 460, 600, 740];
|
||||
const delays = [0.8, 0.6, 0.4, 0.2, 0.0];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
style={{ background: bg }}
|
||||
>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} width="100%" height="100%">
|
||||
<defs>
|
||||
<pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse">
|
||||
<path d="M 24 0 L 0 0 0 24" fill="none" stroke={GRAY_LT} strokeWidth="1" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width={W} height={H} fill="url(#grid)" />
|
||||
|
||||
{xs.map((x, i) => (
|
||||
<ServerBox
|
||||
key={`b-${i}`}
|
||||
x={x}
|
||||
y={rowY}
|
||||
delay={delays[i]}
|
||||
accentPulse
|
||||
/>
|
||||
))}
|
||||
|
||||
<Magnifier x={120} y={rowY} flip={true} delay={0.25} duration={3.2} />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
156
src/pages/network/animations/ProxyForwarding.tsx
Normal file
156
src/pages/network/animations/ProxyForwarding.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import { motion, useReducedMotion } from 'framer-motion';
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
bg?: string;
|
||||
};
|
||||
|
||||
const ACCENT = '#00b8db';
|
||||
const STROKE = '#111827';
|
||||
const GRAY = '#9CA3AF';
|
||||
const GRAY_LT = '#E5E7EB';
|
||||
|
||||
function Laptop({ x, y }: { x: number; y: number }) {
|
||||
return (
|
||||
<g transform={`translate(${x}, ${y})`}>
|
||||
<rect x={-48} y={-32} width={96} height={64} rx={8} fill="#fff" stroke={STROKE} strokeWidth={3} />
|
||||
<rect x={-44} y={-28} width={88} height={40} rx={6} fill={GRAY_LT} />
|
||||
<rect x={-56} y={32} width={112} height={10} rx={5} fill={STROKE} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
function ServerStack({ x, y }: { x: number; y: number }) {
|
||||
return (
|
||||
<g transform={`translate(${x}, ${y})`}>
|
||||
{[0, 1, 2].map((i) => (
|
||||
<g key={i} transform={`translate(0, ${-38 + i * 28})`}>
|
||||
<rect x={-56} y={-12} width={112} height={24} rx={8} fill="#fff" stroke={STROKE} strokeWidth={3} />
|
||||
<rect x={-46} y={-6} width={56} height={12} rx={6} fill={GRAY_LT} />
|
||||
<circle cx={20} cy={0} r={4} fill={GRAY} />
|
||||
<circle cx={30} cy={0} r={4} fill={ACCENT} />
|
||||
<circle cx={40} cy={0} r={4} fill={GRAY} />
|
||||
</g>
|
||||
))}
|
||||
<rect x={-18} y={48} width={36} height={6} rx={3} fill={GRAY} />
|
||||
<rect x={-10} y={54} width={20} height={6} rx={3} fill={ACCENT} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
function Cloud({ x, y }: { x: number; y: number }) {
|
||||
return (
|
||||
<g transform={`translate(${x}, ${y})`} fill={STROKE}>
|
||||
<circle cx={-30} cy={0} r={18} fill={STROKE} />
|
||||
<circle cx={-8} cy={-10} r={22} fill={STROKE} />
|
||||
<circle cx={16} cy={0} r={20} fill={STROKE} />
|
||||
<rect x={-40} y={0} width={72} height={20} rx={10} fill={STROKE} />
|
||||
<rect x={-46} y={18} width={88} height={6} rx={3} fill={STROKE} />
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
function Arrow({ d, delay = 0 }: { d: string; delay?: number }) {
|
||||
return (
|
||||
<motion.path
|
||||
d={d}
|
||||
fill="none"
|
||||
stroke={STROKE}
|
||||
strokeWidth={4}
|
||||
strokeLinecap="round"
|
||||
initial={{ pathLength: 0, opacity: 0.3 }}
|
||||
animate={{ pathLength: 1, opacity: 1 }}
|
||||
transition={{ duration: 0.8, delay, ease: [0.22, 1, 0.36, 1] }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function Packet({
|
||||
xs,
|
||||
ys,
|
||||
delay = 0,
|
||||
color = ACCENT,
|
||||
duration = 2.2,
|
||||
}: {
|
||||
xs: number[];
|
||||
ys: number[];
|
||||
delay?: number;
|
||||
color?: string;
|
||||
duration?: number;
|
||||
}) {
|
||||
const prefersReduced = useReducedMotion();
|
||||
return (
|
||||
<motion.circle
|
||||
r={6}
|
||||
fill={color}
|
||||
initial={{ x: xs[0], y: ys[0], opacity: 0 }}
|
||||
animate={{
|
||||
x: prefersReduced ? xs[0] : xs,
|
||||
y: prefersReduced ? ys[0] : ys,
|
||||
opacity: 1,
|
||||
}}
|
||||
transition={{
|
||||
delay,
|
||||
duration: prefersReduced ? 0.01 : duration,
|
||||
ease: [0.22, 1, 0.36, 1],
|
||||
repeat: prefersReduced ? 0 : Infinity,
|
||||
repeatDelay: 0.6,
|
||||
}}
|
||||
stroke="#fff"
|
||||
strokeWidth={2}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProxyForwarding({ className, bg = '#ffffff' }: Props) {
|
||||
const W = 1000;
|
||||
const H = 420;
|
||||
|
||||
const C1 = { x: 140, y: 90 };
|
||||
const C2 = { x: 140, y: 210 };
|
||||
const C3 = { x: 140, y: 330 };
|
||||
|
||||
const PROXY = { x: 420, y: 210 };
|
||||
const CLOUD = { x: 640, y: 210 };
|
||||
const DEST = { x: 860, y: 210 };
|
||||
|
||||
return (
|
||||
<div className={className} aria-hidden="true" role="img" style={{ background: bg }}>
|
||||
<svg viewBox={`0 0 ${W} ${H}`} width="100%" height="100%">
|
||||
<defs>
|
||||
<pattern id="grid" width="24" height="24" patternUnits="userSpaceOnUse">
|
||||
<path d="M 24 0 L 0 0 0 24" fill="none" stroke={GRAY_LT} strokeWidth="1" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width={W} height={H} fill="url(#grid)" />
|
||||
|
||||
<Laptop x={C1.x} y={C1.y} />
|
||||
<Laptop x={C2.x} y={C2.y} />
|
||||
<Laptop x={C3.x} y={C3.y} />
|
||||
|
||||
<ServerStack x={PROXY.x} y={PROXY.y} />
|
||||
|
||||
<Cloud x={CLOUD.x} y={CLOUD.y} />
|
||||
|
||||
<ServerStack x={DEST.x} y={DEST.y} />
|
||||
|
||||
<Arrow d={`M ${C1.x + 70} ${C1.y} C 260 ${C1.y}, 320 150, ${PROXY.x - 80} 170`} delay={0.05} />
|
||||
<Arrow d={`M ${C2.x + 70} ${C2.y} L ${PROXY.x - 80} ${PROXY.y}`} delay={0.1} />
|
||||
<Arrow d={`M ${C3.x + 70} ${C3.y} C 260 ${C3.y}, 320 270, ${PROXY.x - 80} 250`} delay={0.15} />
|
||||
|
||||
<Arrow d={`M ${PROXY.x + 80} ${PROXY.y} L ${CLOUD.x - 60} ${CLOUD.y}`} delay={0.2} />
|
||||
<Arrow d={`M ${CLOUD.x + 60} ${CLOUD.y} L ${DEST.x - 80} ${DEST.y}`} delay={0.25} />
|
||||
|
||||
<Packet xs={[C1.x + 70, PROXY.x - 80]} ys={[C1.y, 170]} delay={0.0} />
|
||||
<Packet xs={[C2.x + 70, PROXY.x - 80]} ys={[C2.y, PROXY.y]} delay={0.3} color={GRAY} />
|
||||
<Packet xs={[C3.x + 70, PROXY.x - 80]} ys={[C3.y, 250]} delay={0.6} />
|
||||
|
||||
<Packet xs={[PROXY.x + 80, CLOUD.x - 60]} ys={[PROXY.y, CLOUD.y]} delay={0.4} />
|
||||
<Packet xs={[PROXY.x + 80, CLOUD.x - 60]} ys={[PROXY.y, CLOUD.y]} delay={0.9} color={GRAY} />
|
||||
|
||||
<Packet xs={[CLOUD.x + 60, DEST.x - 80]} ys={[CLOUD.y, DEST.y]} delay={0.7} />
|
||||
<Packet xs={[CLOUD.x + 60, DEST.x - 80]} ys={[CLOUD.y, DEST.y]} delay={1.1} color={GRAY} />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user