feat: add UI components for card stack, world map and evervault card with theme support
This commit is contained in:
		
							
								
								
									
										81
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										81
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -20,10 +20,12 @@ | |||||||
|         "class-variance-authority": "^0.7.1", |         "class-variance-authority": "^0.7.1", | ||||||
|         "clsx": "^2.1.1", |         "clsx": "^2.1.1", | ||||||
|         "cobe": "^0.6.5", |         "cobe": "^0.6.5", | ||||||
|  |         "dotted-map": "^2.2.3", | ||||||
|         "framer-motion": "^10.18.0", |         "framer-motion": "^10.18.0", | ||||||
|         "lucide-react": "^0.544.0", |         "lucide-react": "^0.544.0", | ||||||
|         "motion": "^12.23.24", |         "motion": "^12.23.24", | ||||||
|         "next": "^14.2.33", |         "next": "^14.2.33", | ||||||
|  |         "next-themes": "^0.4.6", | ||||||
|         "popmotion": "^11.0.5", |         "popmotion": "^11.0.5", | ||||||
|         "react": "^18.3.1", |         "react": "^18.3.1", | ||||||
|         "react-countup": "^6.5.3", |         "react-countup": "^6.5.3", | ||||||
| @@ -4089,6 +4091,40 @@ | |||||||
|         "url": "https://github.com/sponsors/tannerlinsley" |         "url": "https://github.com/sponsors/tannerlinsley" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@turf/boolean-point-in-polygon": { | ||||||
|  |       "version": "6.5.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz", | ||||||
|  |       "integrity": "sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@turf/helpers": "^6.5.0", | ||||||
|  |         "@turf/invariant": "^6.5.0" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://opencollective.com/turf" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@turf/helpers": { | ||||||
|  |       "version": "6.5.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz", | ||||||
|  |       "integrity": "sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://opencollective.com/turf" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@turf/invariant": { | ||||||
|  |       "version": "6.5.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz", | ||||||
|  |       "integrity": "sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@turf/helpers": "^6.5.0" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://opencollective.com/turf" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/@tybys/wasm-util": { |     "node_modules/@tybys/wasm-util": { | ||||||
|       "version": "0.10.1", |       "version": "0.10.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", |       "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", | ||||||
| @@ -6889,6 +6925,16 @@ | |||||||
|         "@types/trusted-types": "^2.0.7" |         "@types/trusted-types": "^2.0.7" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/dotted-map": { | ||||||
|  |       "version": "2.2.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/dotted-map/-/dotted-map-2.2.3.tgz", | ||||||
|  |       "integrity": "sha512-8hyOOHHLLVCcCisM3yb9hqp+3bJ7TSMcr1SfrUw8Wxp5UMqih35jIvUyagweCooJbz/EH1nC9GGuPysh7+YlAg==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@turf/boolean-point-in-polygon": "^6.0.1", | ||||||
|  |         "proj4": "^2.6.1" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/dunder-proto": { |     "node_modules/dunder-proto": { | ||||||
|       "version": "1.0.1", |       "version": "1.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", |       "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", | ||||||
| @@ -10517,6 +10563,12 @@ | |||||||
|         "uuid": "^11.1.0" |         "uuid": "^11.1.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/mgrs": { | ||||||
|  |       "version": "1.0.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz", | ||||||
|  |       "integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/micromark": { |     "node_modules/micromark": { | ||||||
|       "version": "4.0.2", |       "version": "4.0.2", | ||||||
|       "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", |       "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", | ||||||
| @@ -11531,6 +11583,16 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/next-themes": { | ||||||
|  |       "version": "0.4.6", | ||||||
|  |       "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", | ||||||
|  |       "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "peerDependencies": { | ||||||
|  |         "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", | ||||||
|  |         "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/next/node_modules/@swc/helpers": { |     "node_modules/next/node_modules/@swc/helpers": { | ||||||
|       "version": "0.5.5", |       "version": "0.5.5", | ||||||
|       "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", |       "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", | ||||||
| @@ -12218,6 +12280,19 @@ | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/proj4": { | ||||||
|  |       "version": "2.19.10", | ||||||
|  |       "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.19.10.tgz", | ||||||
|  |       "integrity": "sha512-uL6/C6kA8+ncJAEDmUeV8PmNJcTlRLDZZa4/87CzRpb8My4p+Ame4LhC4G3H/77z2icVqcu3nNL9h5buSdnY+g==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "mgrs": "1.0.0", | ||||||
|  |         "wkt-parser": "^1.5.1" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ahocevar" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/prop-types": { |     "node_modules/prop-types": { | ||||||
|       "version": "15.8.1", |       "version": "15.8.1", | ||||||
|       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", |       "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", | ||||||
| @@ -15508,6 +15583,12 @@ | |||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/wkt-parser": { | ||||||
|  |       "version": "1.5.2", | ||||||
|  |       "resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.5.2.tgz", | ||||||
|  |       "integrity": "sha512-1ZUiV1FTwSiSrgWzV9KXJuOF2BVW91KY/mau04BhnmgOdroRQea7Q0s5TVqwGLm0D2tZwObd/tBYXW49sSxp3Q==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/word-wrap": { |     "node_modules/word-wrap": { | ||||||
|       "version": "1.2.5", |       "version": "1.2.5", | ||||||
|       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", |       "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", | ||||||
|   | |||||||
| @@ -22,10 +22,12 @@ | |||||||
|     "class-variance-authority": "^0.7.1", |     "class-variance-authority": "^0.7.1", | ||||||
|     "clsx": "^2.1.1", |     "clsx": "^2.1.1", | ||||||
|     "cobe": "^0.6.5", |     "cobe": "^0.6.5", | ||||||
|  |     "dotted-map": "^2.2.3", | ||||||
|     "framer-motion": "^10.18.0", |     "framer-motion": "^10.18.0", | ||||||
|     "lucide-react": "^0.544.0", |     "lucide-react": "^0.544.0", | ||||||
|     "motion": "^12.23.24", |     "motion": "^12.23.24", | ||||||
|     "next": "^14.2.33", |     "next": "^14.2.33", | ||||||
|  |     "next-themes": "^0.4.6", | ||||||
|     "popmotion": "^11.0.5", |     "popmotion": "^11.0.5", | ||||||
|     "react": "^18.3.1", |     "react": "^18.3.1", | ||||||
|     "react-countup": "^6.5.3", |     "react-countup": "^6.5.3", | ||||||
|   | |||||||
							
								
								
									
										48
									
								
								src/components/ui/card-stack.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/components/ui/card-stack.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | "use client"; | ||||||
|  | import { motion } from "framer-motion"; | ||||||
|  |  | ||||||
|  | type Card = { | ||||||
|  |   id: number; | ||||||
|  |   name: string; | ||||||
|  |   description: string; | ||||||
|  |   icon: React.ReactNode; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const CardStack = ({ | ||||||
|  |   items, | ||||||
|  |   offset, | ||||||
|  |   scaleFactor, | ||||||
|  | }: { | ||||||
|  |   items: Card[]; | ||||||
|  |   offset?: number; | ||||||
|  |   scaleFactor?: number; | ||||||
|  | }) => { | ||||||
|  |   const CARD_OFFSET = offset || 10; | ||||||
|  |   const HORIZONTAL_OFFSET = 336; // Adjusted for 1/8 overlap | ||||||
|  |   const SCALE_FACTOR = scaleFactor || 0.06; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="relative h-[20rem] w-full flex items-center justify-center"> | ||||||
|  |       {items.map((card, index) => ( | ||||||
|  |         <motion.div | ||||||
|  |           key={card.id} | ||||||
|  |           className="absolute dark:bg-black bg-white h-[16rem] w-[24rem] rounded-3xl p-4 shadow-xl border border-neutral-200 dark:border-white/[0.1] shadow-black/[0.1] dark:shadow-white/[0.05] flex flex-col justify-between" | ||||||
|  |           style={{ | ||||||
|  |             transformOrigin: "top center", | ||||||
|  |           }} | ||||||
|  |           animate={{ | ||||||
|  |             top: index * -CARD_OFFSET, | ||||||
|  |             left: index * HORIZONTAL_OFFSET, | ||||||
|  |             scale: 1 - index * SCALE_FACTOR, | ||||||
|  |             zIndex: items.length - index, | ||||||
|  |           }} | ||||||
|  |         > | ||||||
|  |           <div className="flex flex-col p-4"> | ||||||
|  |             <h3 className="text-base/7 font-semibold text-gray-900 dark:text-white">{card.name}</h3> | ||||||
|  |             <p className="mt-1 flex-auto text-base/7 text-gray-600 dark:text-neutral-300">{card.description}</p> | ||||||
|  |           </div> | ||||||
|  |         </motion.div> | ||||||
|  |       ))} | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
							
								
								
									
										106
									
								
								src/components/ui/evervault-card.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/components/ui/evervault-card.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | "use client"; | ||||||
|  | import { useMotionValue } from "motion/react"; | ||||||
|  | import React, { useState, useEffect } from "react"; | ||||||
|  | import { useMotionTemplate, motion } from "motion/react"; | ||||||
|  | import { cn } from "@/lib/utils"; | ||||||
|  |  | ||||||
|  | export const EvervaultCard = ({ | ||||||
|  |   children, | ||||||
|  |   className, | ||||||
|  | }: { | ||||||
|  |   children?: React.ReactNode; | ||||||
|  |   className?: string; | ||||||
|  | }) => { | ||||||
|  |   let mouseX = useMotionValue(0); | ||||||
|  |   let mouseY = useMotionValue(0); | ||||||
|  |  | ||||||
|  |   const [randomString, setRandomString] = useState(""); | ||||||
|  |  | ||||||
|  |   useEffect(() => { | ||||||
|  |     let str = generateRandomString(1500); | ||||||
|  |     setRandomString(str); | ||||||
|  |   }, []); | ||||||
|  |  | ||||||
|  |   function onMouseMove({ currentTarget, clientX, clientY }: any) { | ||||||
|  |     let { left, top } = currentTarget.getBoundingClientRect(); | ||||||
|  |     mouseX.set(clientX - left); | ||||||
|  |     mouseY.set(clientY - top); | ||||||
|  |  | ||||||
|  |     const str = generateRandomString(1500); | ||||||
|  |     setRandomString(str); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div | ||||||
|  |       className={cn( | ||||||
|  |         "p-0.5  bg-transparent aspect-square  flex items-center justify-center w-full h-full relative", | ||||||
|  |         className | ||||||
|  |       )} | ||||||
|  |     > | ||||||
|  |       <div | ||||||
|  |         onMouseMove={onMouseMove} | ||||||
|  |         className="group/card rounded-3xl w-full relative overflow-hidden bg-transparent flex items-center justify-center h-full" | ||||||
|  |       > | ||||||
|  |         <CardPattern | ||||||
|  |           mouseX={mouseX} | ||||||
|  |           mouseY={mouseY} | ||||||
|  |           randomString={randomString} | ||||||
|  |         /> | ||||||
|  |         <div className="relative z-10 flex items-center justify-center"> | ||||||
|  |           <div className="relative p-4"> | ||||||
|  |             {children} | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export function CardPattern({ mouseX, mouseY, randomString }: any) { | ||||||
|  |   let maskImage = useMotionTemplate`radial-gradient(250px at ${mouseX}px ${mouseY}px, white, transparent)`; | ||||||
|  |   let style = { maskImage, WebkitMaskImage: maskImage }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="pointer-events-none"> | ||||||
|  |       <div className="absolute inset-0 rounded-2xl  [mask-image:linear-gradient(white,transparent)] group-hover/card:opacity-50"></div> | ||||||
|  |       <motion.div | ||||||
|  |         className="absolute inset-0 rounded-2xl bg-gradient-to-r from-green-500 to-blue-700 opacity-0  group-hover/card:opacity-100 backdrop-blur-xl transition duration-500" | ||||||
|  |         style={style} | ||||||
|  |       /> | ||||||
|  |       <motion.div | ||||||
|  |         className="absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay  group-hover/card:opacity-100" | ||||||
|  |         style={style} | ||||||
|  |       > | ||||||
|  |         <p className="absolute inset-x-0 text-xs h-full break-words whitespace-pre-wrap text-white font-mono font-bold transition duration-500"> | ||||||
|  |           {randomString} | ||||||
|  |         </p> | ||||||
|  |       </motion.div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const characters = | ||||||
|  |   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||||||
|  | export const generateRandomString = (length: number) => { | ||||||
|  |   let result = ""; | ||||||
|  |   for (let i = 0; i < length; i++) { | ||||||
|  |     result += characters.charAt(Math.floor(Math.random() * characters.length)); | ||||||
|  |   } | ||||||
|  |   return result; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const Icon = ({ className, ...rest }: any) => { | ||||||
|  |   return ( | ||||||
|  |     <svg | ||||||
|  |       xmlns="http://www.w3.org/2000/svg" | ||||||
|  |       fill="none" | ||||||
|  |       viewBox="0 0 24 24" | ||||||
|  |       strokeWidth="1.5" | ||||||
|  |       stroke="currentColor" | ||||||
|  |       className={className} | ||||||
|  |       {...rest} | ||||||
|  |     > | ||||||
|  |       <path strokeLinecap="round" strokeLinejoin="round" d="M12 6v12m6-6H6" /> | ||||||
|  |     </svg> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
							
								
								
									
										170
									
								
								src/components/ui/world-map.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								src/components/ui/world-map.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,170 @@ | |||||||
|  | "use client"; | ||||||
|  |  | ||||||
|  | import { useRef } from "react"; | ||||||
|  | import { motion } from "motion/react"; | ||||||
|  | import DottedMap from "dotted-map"; | ||||||
|  |  | ||||||
|  | import { useTheme } from "next-themes"; | ||||||
|  |  | ||||||
|  | interface MapProps { | ||||||
|  |   dots?: Array<{ | ||||||
|  |     start: { lat: number; lng: number; label?: string }; | ||||||
|  |     end: { lat: number; lng: number; label?: string }; | ||||||
|  |   }>; | ||||||
|  |   lineColor?: string; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default function WorldMap({ | ||||||
|  |   dots = [], | ||||||
|  |   lineColor = "#06b6d4", | ||||||
|  | }: MapProps) { | ||||||
|  |   const svgRef = useRef<SVGSVGElement>(null); | ||||||
|  |   const map = new DottedMap({ height: 100, grid: "diagonal" }); | ||||||
|  |  | ||||||
|  |   const { theme } = useTheme(); | ||||||
|  |  | ||||||
|  |   const svgMap = map.getSVG({ | ||||||
|  |     radius: 0.22, | ||||||
|  |     color: theme === "dark" ? "#FFFFFF40" : "#00000040", | ||||||
|  |     shape: "circle", | ||||||
|  |     backgroundColor: theme === "dark" ? "black" : "white", | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   const projectPoint = (lat: number, lng: number) => { | ||||||
|  |     const x = (lng + 180) * (800 / 360); | ||||||
|  |     const y = (90 - lat) * (400 / 180); | ||||||
|  |     return { x, y }; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   const createCurvedPath = ( | ||||||
|  |     start: { x: number; y: number }, | ||||||
|  |     end: { x: number; y: number } | ||||||
|  |   ) => { | ||||||
|  |     const midX = (start.x + end.x) / 2; | ||||||
|  |     const midY = Math.min(start.y, end.y) - 50; | ||||||
|  |     return `M ${start.x} ${start.y} Q ${midX} ${midY} ${end.x} ${end.y}`; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="w-full aspect-[2/1] dark:bg-black bg-white rounded-lg  relative font-sans"> | ||||||
|  |       <img | ||||||
|  |         src={`data:image/svg+xml;utf8,${encodeURIComponent(svgMap)}`} | ||||||
|  |         className="h-full w-full [mask-image:linear-gradient(to_bottom,transparent,white_10%,white_90%,transparent)] pointer-events-none select-none" | ||||||
|  |         alt="world map" | ||||||
|  |         height="495" | ||||||
|  |         width="1056" | ||||||
|  |         draggable={false} | ||||||
|  |       /> | ||||||
|  |       <svg | ||||||
|  |         ref={svgRef} | ||||||
|  |         viewBox="0 0 800 400" | ||||||
|  |         className="w-full h-full absolute inset-0 pointer-events-none select-none" | ||||||
|  |       > | ||||||
|  |         {dots.map((dot, i) => { | ||||||
|  |           const startPoint = projectPoint(dot.start.lat, dot.start.lng); | ||||||
|  |           const endPoint = projectPoint(dot.end.lat, dot.end.lng); | ||||||
|  |           return ( | ||||||
|  |             <g key={`path-group-${i}`}> | ||||||
|  |               <motion.path | ||||||
|  |                 d={createCurvedPath(startPoint, endPoint)} | ||||||
|  |                 fill="none" | ||||||
|  |                 stroke="url(#path-gradient)" | ||||||
|  |                 strokeWidth="1" | ||||||
|  |                 initial={{ | ||||||
|  |                   pathLength: 0, | ||||||
|  |                 }} | ||||||
|  |                 animate={{ | ||||||
|  |                   pathLength: 1, | ||||||
|  |                 }} | ||||||
|  |                 transition={{ | ||||||
|  |                   duration: 1, | ||||||
|  |                   delay: 0.5 * i, | ||||||
|  |                   ease: "easeOut", | ||||||
|  |                 }} | ||||||
|  |                 key={`start-upper-${i}`} | ||||||
|  |               ></motion.path> | ||||||
|  |             </g> | ||||||
|  |           ); | ||||||
|  |         })} | ||||||
|  |  | ||||||
|  |         <defs> | ||||||
|  |           <linearGradient id="path-gradient" x1="0%" y1="0%" x2="100%" y2="0%"> | ||||||
|  |             <stop offset="0%" stopColor="white" stopOpacity="0" /> | ||||||
|  |             <stop offset="5%" stopColor={lineColor} stopOpacity="1" /> | ||||||
|  |             <stop offset="95%" stopColor={lineColor} stopOpacity="1" /> | ||||||
|  |             <stop offset="100%" stopColor="white" stopOpacity="0" /> | ||||||
|  |           </linearGradient> | ||||||
|  |         </defs> | ||||||
|  |  | ||||||
|  |         {dots.map((dot, i) => ( | ||||||
|  |           <g key={`points-group-${i}`}> | ||||||
|  |             <g key={`start-${i}`}> | ||||||
|  |               <circle | ||||||
|  |                 cx={projectPoint(dot.start.lat, dot.start.lng).x} | ||||||
|  |                 cy={projectPoint(dot.start.lat, dot.start.lng).y} | ||||||
|  |                 r="2" | ||||||
|  |                 fill={lineColor} | ||||||
|  |               /> | ||||||
|  |               <circle | ||||||
|  |                 cx={projectPoint(dot.start.lat, dot.start.lng).x} | ||||||
|  |                 cy={projectPoint(dot.start.lat, dot.start.lng).y} | ||||||
|  |                 r="2" | ||||||
|  |                 fill={lineColor} | ||||||
|  |                 opacity="0.5" | ||||||
|  |               > | ||||||
|  |                 <animate | ||||||
|  |                   attributeName="r" | ||||||
|  |                   from="2" | ||||||
|  |                   to="8" | ||||||
|  |                   dur="1.5s" | ||||||
|  |                   begin="0s" | ||||||
|  |                   repeatCount="indefinite" | ||||||
|  |                 /> | ||||||
|  |                 <animate | ||||||
|  |                   attributeName="opacity" | ||||||
|  |                   from="0.5" | ||||||
|  |                   to="0" | ||||||
|  |                   dur="1.5s" | ||||||
|  |                   begin="0s" | ||||||
|  |                   repeatCount="indefinite" | ||||||
|  |                 /> | ||||||
|  |               </circle> | ||||||
|  |             </g> | ||||||
|  |             <g key={`end-${i}`}> | ||||||
|  |               <circle | ||||||
|  |                 cx={projectPoint(dot.end.lat, dot.end.lng).x} | ||||||
|  |                 cy={projectPoint(dot.end.lat, dot.end.lng).y} | ||||||
|  |                 r="2" | ||||||
|  |                 fill={lineColor} | ||||||
|  |               /> | ||||||
|  |               <circle | ||||||
|  |                 cx={projectPoint(dot.end.lat, dot.end.lng).x} | ||||||
|  |                 cy={projectPoint(dot.end.lat, dot.end.lng).y} | ||||||
|  |                 r="2" | ||||||
|  |                 fill={lineColor} | ||||||
|  |                 opacity="0.5" | ||||||
|  |               > | ||||||
|  |                 <animate | ||||||
|  |                   attributeName="r" | ||||||
|  |                   from="2" | ||||||
|  |                   to="8" | ||||||
|  |                   dur="1.5s" | ||||||
|  |                   begin="0s" | ||||||
|  |                   repeatCount="indefinite" | ||||||
|  |                 /> | ||||||
|  |                 <animate | ||||||
|  |                   attributeName="opacity" | ||||||
|  |                   from="0.5" | ||||||
|  |                   to="0" | ||||||
|  |                   dur="1.5s" | ||||||
|  |                   begin="0s" | ||||||
|  |                   repeatCount="indefinite" | ||||||
|  |                 /> | ||||||
|  |               </circle> | ||||||
|  |             </g> | ||||||
|  |           </g> | ||||||
|  |         ))} | ||||||
|  |       </svg> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
							
								
								
									
										
											BIN
										
									
								
								src/images/cloudlayer.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/images/cloudlayer.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 133 KiB | 
| @@ -16,7 +16,7 @@ export function HomeAurora() { | |||||||
|           duration: 1, |           duration: 1, | ||||||
|           ease: "easeInOut", |           ease: "easeInOut", | ||||||
|         }} |         }} | ||||||
|         className="relative mb-20 -top-5 flex flex-col items-center justify-center gap-4 px-4 max-w-5xl" |         className="relative mb-20  flex flex-col items-center justify-center gap-4 px-4 max-w-5xl" | ||||||
|       > |       > | ||||||
|         <div className="text-center text-gray-800"> |         <div className="text-center text-gray-800"> | ||||||
|           <H1>Decentralized Autonomous <span className="font-neuton text-bold lg:text-8xl italic">Agentic Cloud.</span></H1> |           <H1>Decentralized Autonomous <span className="font-neuton text-bold lg:text-8xl italic">Agentic Cloud.</span></H1> | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								src/pages/home/HomeFeatures.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/pages/home/HomeFeatures.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | import { InboxIcon, TrashIcon, UsersIcon } from '@heroicons/react/24/outline' | ||||||
|  | import { H2, P } from '@/components/Texts' | ||||||
|  | import { CardStack } from '@/components/ui/card-stack' | ||||||
|  |  | ||||||
|  | const features = [ | ||||||
|  |   { | ||||||
|  |     name: 'Network Layer', | ||||||
|  |     description: | ||||||
|  |       "A global, end-to-end encrypted overlay that simply doesn't break. Shortest-path routing moves your traffic the fastest way, every time with instant discovery.", | ||||||
|  |     href: '#', | ||||||
|  |     icon: UsersIcon, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: 'Cloud Layer', | ||||||
|  |     description: | ||||||
|  |       'An autonomous, stateless OS that enforces pre-deterministic deployments you define. Workloads are cryptographically bound to your private key—location and access are yours.', | ||||||
|  |     href: '#', | ||||||
|  |     icon: TrashIcon, | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     name: 'Agent Layer', | ||||||
|  |     description: | ||||||
|  |       'Your sovereign agent with private memory and permissioned data access—always under your control. Choose from a wide library of open-source LLMs, paired with built-in semantic search and retrieval.', | ||||||
|  |     href: '#', | ||||||
|  |     icon: InboxIcon, | ||||||
|  |   }, | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | export function HomeFeatures() { | ||||||
|  |   const cards = features.map((feature, index) => ({ | ||||||
|  |     id: index, | ||||||
|  |     name: feature.name, | ||||||
|  |     description: feature.description, | ||||||
|  |     icon: <feature.icon aria-hidden="true" className="size-6 text-white" />, | ||||||
|  |   })); | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <div className="bg-white py-24"> | ||||||
|  |       <div className="mx-auto max-w-7xl px-6 lg:px-8"> | ||||||
|  |         <div className="mx-auto max-w-2xl lg:mx-0"> | ||||||
|  |           <H2 className=""> | ||||||
|  |             The Mycelium <span className="font-neuton font-bold italic">Stack</span> | ||||||
|  |           </H2> | ||||||
|  |           <P className="mt-6 "> | ||||||
|  |             Built with Mycelium technology, our AI infrastructure ensures unbreakable networks, complete data sovereignty, ultra-secure agent-human communication, and unhackable data storage systems. | ||||||
|  |           </P> | ||||||
|  |         </div> | ||||||
|  |         <div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-32 lg:max-w-7xl"> | ||||||
|  |           <div className="flex items-center justify-center w-full"> | ||||||
|  |             <CardStack items={cards} offset={80} /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ) | ||||||
|  | } | ||||||
							
								
								
									
										69
									
								
								src/pages/home/HomeMap.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/pages/home/HomeMap.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | "use client"; | ||||||
|  | import WorldMap from "@/components/ui/world-map"; | ||||||
|  | import { motion } from "motion/react"; | ||||||
|  | import { H2, P } from "@/components/Texts"; | ||||||
|  |  | ||||||
|  | export function HomeMapSection() { | ||||||
|  |   return ( | ||||||
|  |     <div className=" py-24 dark:bg-black bg-white w-full"> | ||||||
|  |       <div className="max-w-7xl mx-auto text-center"> | ||||||
|  |         <H2 className="font-medium text-xl md:text-4xl dark:text-white text-gray-800"> | ||||||
|  |           Mycelium Network is{" "} | ||||||
|  |           <span className="text-black font-neuton text-bold italic"> | ||||||
|  |             {"Live.".split("").map((word, idx) => ( | ||||||
|  |               <motion.span | ||||||
|  |                 key={idx} | ||||||
|  |                 className="inline-block" | ||||||
|  |                 initial={{ x: -10, opacity: 0 }} | ||||||
|  |                 animate={{ x: 0, opacity: 1 }} | ||||||
|  |                 transition={{ duration: 0.5, delay: idx * 0.04 }} | ||||||
|  |               > | ||||||
|  |                 {word} | ||||||
|  |               </motion.span> | ||||||
|  |             ))} | ||||||
|  |           </span> | ||||||
|  |         </H2> | ||||||
|  |         <P className="text-sm md:text-lg text-neutral-500 max-w-2xl mx-auto py-4"> | ||||||
|  |           Mycelium Network's advancement technology enables anyone to deploy | ||||||
|  |             their own Internet infrastructure, anywhere. | ||||||
|  |         </P> | ||||||
|  |       </div> | ||||||
|  |       <div className="max-w-5xl mx-auto"> | ||||||
|  |         <WorldMap | ||||||
|  |         dots={[ | ||||||
|  |           { | ||||||
|  |             start: { | ||||||
|  |               lat: 64.2008, | ||||||
|  |               lng: -149.4937, | ||||||
|  |             }, // Alaska (Fairbanks) | ||||||
|  |             end: { | ||||||
|  |               lat: 34.0522, | ||||||
|  |               lng: -118.2437, | ||||||
|  |             }, // Los Angeles | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             start: { lat: 64.2008, lng: -149.4937 }, // Alaska (Fairbanks) | ||||||
|  |             end: { lat: -15.7975, lng: -47.8919 }, // Brazil (Brasília) | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             start: { lat: -15.7975, lng: -47.8919 }, // Brazil (Brasília) | ||||||
|  |             end: { lat: 38.7223, lng: -9.1393 }, // Lisbon | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             start: { lat: 51.5074, lng: -0.1278 }, // London | ||||||
|  |             end: { lat: 28.6139, lng: 77.209 }, // New Delhi | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             start: { lat: 28.6139, lng: 77.209 }, // New Delhi | ||||||
|  |             end: { lat: 43.1332, lng: 131.9113 }, // Vladivostok | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             start: { lat: 28.6139, lng: 77.209 }, // New Delhi | ||||||
|  |             end: { lat: -1.2921, lng: 36.8219 }, // Nairobi | ||||||
|  |           }, | ||||||
|  |         ]} | ||||||
|  |       /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -5,6 +5,8 @@ import { StackSection } from './StackSection' | |||||||
| import { HomeHeroLight2 } from './HomeHeroLight2' | import { HomeHeroLight2 } from './HomeHeroLight2' | ||||||
| import { HomeHeroDark } from './HomeHeroDark' | import { HomeHeroDark } from './HomeHeroDark' | ||||||
| import { HomeAurora } from './HomeAurora' | import { HomeAurora } from './HomeAurora' | ||||||
|  | import { HomeMapSection } from './HomeMap' | ||||||
|  | import { HomeFeatures } from './HomeFeatures' | ||||||
|  |  | ||||||
| export default function HomePage() { | export default function HomePage() { | ||||||
|   return ( |   return ( | ||||||
| @@ -14,7 +16,11 @@ export default function HomePage() { | |||||||
|       </AnimatedSection> |       </AnimatedSection> | ||||||
|  |  | ||||||
|       <AnimatedSection id="next-section"> |       <AnimatedSection id="next-section"> | ||||||
|         <WorldMapSection /> |         <HomeFeatures /> | ||||||
|  |       </AnimatedSection> | ||||||
|  |  | ||||||
|  |       <AnimatedSection > | ||||||
|  |         <HomeMapSection /> | ||||||
|       </AnimatedSection> |       </AnimatedSection> | ||||||
|  |  | ||||||
|       <AnimatedSection> |       <AnimatedSection> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user