chore: archive unused images and add storage feature components
- Moved 35 legacy image assets to archive directory for cleanup - Created StorageCoreValue component showcasing Digital Me blueprint with logo grid - Added Encrypted animation component visualizing secure storage with data movement
|
Before Width: | Height: | Size: 497 KiB After Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 431 KiB After Width: | Height: | Size: 431 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 878 KiB After Width: | Height: | Size: 878 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 938 KiB After Width: | Height: | Size: 938 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 316 KiB After Width: | Height: | Size: 316 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
61
src/pages/storage/StorageCoreValue.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Container } from "@/components/Container";
|
||||||
|
import { H3, P, Eyebrow } from "@/components/Texts";
|
||||||
|
|
||||||
|
export function StorageCoreValue() {
|
||||||
|
const logos = [
|
||||||
|
{ src: '/images/logo/cryptpad.png', href: 'https://cryptpad.fr' },
|
||||||
|
{ src: '/images/logo/gitea.png', href: 'https://about.gitea.com' },
|
||||||
|
{ src: '/images/logo/lifekit.png', href: '#' }, // No link available
|
||||||
|
{ src: '/images/logo/matrix.png', href: 'https://matrix.org' },
|
||||||
|
{ src: '/images/logo/nextcloud.png', href: 'https://nextcloud.com' },
|
||||||
|
{ src: '/images/logo/stalwart.png', href: 'https://stalw.art' },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="w-full max-w-8xl mx-auto bg-transparent">
|
||||||
|
|
||||||
|
{/* ✅ Boxed container */}
|
||||||
|
<div className="max-w-7xl bg-white mx-auto py-12 border border-t-0 border-b-0 border-gray-100">
|
||||||
|
<Container>
|
||||||
|
<div className="mx-auto max-w-4xl sm:text-center">
|
||||||
|
<Eyebrow className="text-cyan-500">Featured Blueprint</Eyebrow>
|
||||||
|
|
||||||
|
<H3 className="text-3xl lg:text-4xl font-medium tracking-tight text-gray-900 mt-2">
|
||||||
|
Your Personal Sovereign Cloud Workspace
|
||||||
|
</H3>
|
||||||
|
|
||||||
|
<P className="mt-6 text-lg text-gray-600">
|
||||||
|
Digital Me is an example environment built to demonstrate what’s possible on top of the Mycelium Stack — a full personal cloud you can deploy, customize, or extend. Your files, communication, apps, and optional AI agent, all running privately on infrastructure you choose.
|
||||||
|
</P>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ✅ 3x2 logo grid */}
|
||||||
|
<div className="mt-12 grid grid-cols-3 gap-x-8 gap-y-12">
|
||||||
|
{logos.map((logo, i) => (
|
||||||
|
<div key={i} className="flex justify-center">
|
||||||
|
<a
|
||||||
|
href={logo.href}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="transition-transform duration-300 hover:scale-105"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={logo.src}
|
||||||
|
alt={`Logo ${i + 1}`}
|
||||||
|
className="max-h-12 w-auto object-contain"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ✅ Bottom line + bottom spacer */}
|
||||||
|
<div className="w-full border-b border-gray-100" />
|
||||||
|
<div className="max-w-7xl mx-auto py-6 border border-t-0 border-b-0 border-gray-100 bg-transparent" />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
193
src/pages/storage/animation/Encrypted.tsx
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { motion, useReducedMotion } from "framer-motion";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
className?: string;
|
||||||
|
accent?: string;
|
||||||
|
gridStroke?: string;
|
||||||
|
stroke?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const W = 760;
|
||||||
|
const H = 420;
|
||||||
|
|
||||||
|
export default function Encrypted({
|
||||||
|
className,
|
||||||
|
accent = "#00b8db",
|
||||||
|
gridStroke = "#e5e7eb",
|
||||||
|
stroke = "#111111",
|
||||||
|
}: Props) {
|
||||||
|
const prefers = useReducedMotion();
|
||||||
|
|
||||||
|
// positions of storage vaults (cylindrical stack illusion)
|
||||||
|
const vaults = [
|
||||||
|
{ x: 200, y: 220 },
|
||||||
|
{ x: 380, y: 180 },
|
||||||
|
{ x: 560, y: 220 },
|
||||||
|
];
|
||||||
|
|
||||||
|
// path representing encrypted data moving across storage nodes
|
||||||
|
const dataPath = `M ${vaults[0].x + 60} ${vaults[0].y}
|
||||||
|
C 280 160, 480 160, ${vaults[2].x} ${vaults[2].y}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={clsx("relative overflow-hidden", className)}
|
||||||
|
aria-hidden="true"
|
||||||
|
role="img"
|
||||||
|
aria-label="Mycelium Storage: encrypted and verifiable at rest and in motion"
|
||||||
|
style={{ background: "transparent" }}
|
||||||
|
>
|
||||||
|
<svg viewBox={`0 0 ${W} ${H}`} className="w-full h-full">
|
||||||
|
{/* === Background Grid (light mode) === */}
|
||||||
|
<defs>
|
||||||
|
<pattern id="grid-light" width="28" height="28" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M 28 0 L 0 0 0 28" fill="none" stroke={gridStroke} strokeWidth="1" opacity="0.6" />
|
||||||
|
</pattern>
|
||||||
|
|
||||||
|
<filter id="cyan-glow">
|
||||||
|
<feGaussianBlur stdDeviation="3" result="blur" />
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="blur" />
|
||||||
|
<feMergeNode in="SourceGraphic" />
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width={W} height={H} fill="url(#grid-light)" />
|
||||||
|
|
||||||
|
{/* === Storage Vaults (cylinders) === */}
|
||||||
|
{vaults.map((v, i) => (
|
||||||
|
<g key={i} transform={`translate(${v.x}, ${v.y})`}>
|
||||||
|
{/* Top ellipse */}
|
||||||
|
<ellipse cx="0" cy="-30" rx="50" ry="14" fill="none" stroke={stroke} strokeWidth="2" />
|
||||||
|
{/* Body */}
|
||||||
|
<rect
|
||||||
|
x="-50"
|
||||||
|
y="-30"
|
||||||
|
width="100"
|
||||||
|
height="60"
|
||||||
|
rx="12"
|
||||||
|
fill="none"
|
||||||
|
stroke={stroke}
|
||||||
|
strokeWidth="2"
|
||||||
|
/>
|
||||||
|
{/* Bottom ellipse (front face) */}
|
||||||
|
<ellipse cx="0" cy="30" rx="50" ry="14" fill="none" stroke={stroke} strokeWidth="2" />
|
||||||
|
{/* subtle cyan glow ring to mark encryption */}
|
||||||
|
<motion.ellipse
|
||||||
|
cx="0"
|
||||||
|
cy="0"
|
||||||
|
rx="55"
|
||||||
|
ry="18"
|
||||||
|
fill="none"
|
||||||
|
stroke={accent}
|
||||||
|
strokeWidth="2"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: [0.15, 0.4, 0.15] }}
|
||||||
|
transition={{
|
||||||
|
delay: i * 0.3,
|
||||||
|
duration: 2.2,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: [0.22, 1, 0.36, 1],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* === Encrypted data movement path === */}
|
||||||
|
<motion.path
|
||||||
|
d={dataPath}
|
||||||
|
fill="none"
|
||||||
|
stroke={stroke}
|
||||||
|
strokeWidth={2}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeDasharray="6"
|
||||||
|
opacity="0.5"
|
||||||
|
initial={{ pathLength: 0 }}
|
||||||
|
animate={{ pathLength: 1 }}
|
||||||
|
transition={{ duration: 1.2, ease: [0.22, 1, 0.36, 1] }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* === Encrypted packets traveling === */}
|
||||||
|
{!prefers && (
|
||||||
|
<motion.circle
|
||||||
|
r={6}
|
||||||
|
fill={accent}
|
||||||
|
style={{ offsetPath: `path('${dataPath}')` }}
|
||||||
|
initial={{ offsetDistance: "0%", opacity: 0 }}
|
||||||
|
animate={{
|
||||||
|
offsetDistance: ["0%", "100%"],
|
||||||
|
opacity: [0.3, 1, 0.3],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 2.8,
|
||||||
|
repeat: Infinity,
|
||||||
|
repeatType: "loop",
|
||||||
|
ease: "linear",
|
||||||
|
}}
|
||||||
|
filter="url(#cyan-glow)"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* === Verification shield (top-right) === */}
|
||||||
|
<motion.path
|
||||||
|
d={`M 620 120
|
||||||
|
L 645 135
|
||||||
|
L 640 165
|
||||||
|
L 620 175
|
||||||
|
L 600 165
|
||||||
|
L 595 135
|
||||||
|
Z`}
|
||||||
|
fill="none"
|
||||||
|
stroke={accent}
|
||||||
|
strokeWidth={2.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
initial={{ pathLength: 0, opacity: 0 }}
|
||||||
|
animate={{ pathLength: 1, opacity: 1 }}
|
||||||
|
transition={{ duration: 1, delay: 0.6, ease: [0.22, 1, 0.36, 1] }}
|
||||||
|
filter="url(#cyan-glow)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* === Verification checkmark === */}
|
||||||
|
<motion.path
|
||||||
|
d="M 606 150 L 617 162 L 635 140"
|
||||||
|
fill="none"
|
||||||
|
stroke={accent}
|
||||||
|
strokeWidth={3}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
initial={{ pathLength: 0 }}
|
||||||
|
animate={{ pathLength: 1 }}
|
||||||
|
transition={{ duration: 0.8, delay: 1.2 }}
|
||||||
|
filter="url(#cyan-glow)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* === Cyan verification pulse === */}
|
||||||
|
{!prefers && (
|
||||||
|
<motion.circle
|
||||||
|
cx="620"
|
||||||
|
cy="150"
|
||||||
|
r="24"
|
||||||
|
stroke={accent}
|
||||||
|
strokeWidth="2"
|
||||||
|
fill="none"
|
||||||
|
initial={{ scale: 1, opacity: 0 }}
|
||||||
|
animate={{
|
||||||
|
scale: [1, 1.25, 1.4],
|
||||||
|
opacity: [0.25, 0.6, 0],
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
duration: 1.8,
|
||||||
|
repeat: Infinity,
|
||||||
|
ease: [0.22, 1, 0.36, 1],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||