From b08de471aef4de7d8b76b39f5b4aeae522eb1160 Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Thu, 25 Sep 2025 15:36:30 +0200 Subject: [PATCH 01/21] replace logo --- src/components/DownloadLink.tsx | 2 +- src/components/Footer.tsx | 3 +- src/components/Header.tsx | 2 +- src/components/Logo.tsx | 115 ++++++++++++++++++++++++++++---- 4 files changed, 104 insertions(+), 18 deletions(-) diff --git a/src/components/DownloadLink.tsx b/src/components/DownloadLink.tsx index 70aa105..07938e7 100644 --- a/src/components/DownloadLink.tsx +++ b/src/components/DownloadLink.tsx @@ -6,7 +6,7 @@ export function DownloadLink() { Get Mycelium diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 378a3f4..d8587c3 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -4,7 +4,6 @@ import Link from 'next/link' import { Button } from '@/components/Button' import { Container } from '@/components/Container' import { TextField } from '@/components/Fields' -import { Logomark } from '@/components/Logo' import { NavLinks } from '@/components/NavLinks' import qrCode from '@/images/qr-code.svg' @@ -27,7 +26,7 @@ export function Footer() {
- + Mycelium Logomark

Mycelium

Unleash the Power of Decentralized Networks

diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 08e87f0..6ca40d7 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -126,7 +126,7 @@ export function Header() { - +
diff --git a/src/components/Logo.tsx b/src/components/Logo.tsx index 8267549..b1255ff 100644 --- a/src/components/Logo.tsx +++ b/src/components/Logo.tsx @@ -1,23 +1,110 @@ export function Logomark(props: React.ComponentPropsWithoutRef<'svg'>) { return ( - + + + + + + + + + + + + + + + + + + + + + + + ) } export function Logo(props: React.ComponentPropsWithoutRef<'svg'>) { return ( - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } From 409535d9dc9c16ae81e4a70db8cde543f0acfe9f Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Thu, 25 Sep 2025 16:27:28 +0200 Subject: [PATCH 02/21] ok --- src/components/Header.tsx | 2 +- src/components/Hero.tsx | 13 ++++++++----- src/components/PhoneFrame.tsx | 32 +++++++------------------------- 3 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 6ca40d7..d85b851 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -139,7 +139,7 @@ export function Header() { - +
diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 7994fed..1ba18a2 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -2,11 +2,9 @@ import { useId } from 'react' import Image from 'next/image' import clsx from 'clsx' -import { AppDemo } from '@/components/AppDemo' import { DownloadLink } from '@/components/DownloadLink' import { Button } from '@/components/Button' import { Container } from '@/components/Container' -import { PhoneFrame } from '@/components/PhoneFrame' import logoBbc from '@/images/logos/bbc.svg' import logoCbs from '@/images/logos/cbs.svg' import logoCnn from '@/images/logos/cnn.svg' @@ -130,9 +128,14 @@ export function Hero() {
- - - + Mycelium application demo
diff --git a/src/components/PhoneFrame.tsx b/src/components/PhoneFrame.tsx index 6ca4cb5..3759948 100644 --- a/src/components/PhoneFrame.tsx +++ b/src/components/PhoneFrame.tsx @@ -1,22 +1,6 @@ import Image from 'next/image' import clsx from 'clsx' -import frame from '@/images/phone-frame.svg' - -function PlaceholderFrame(props: React.ComponentPropsWithoutRef<'svg'>) { - return ( - - ) -} - export function PhoneFrame({ className, children, @@ -24,19 +8,17 @@ export function PhoneFrame({ ...props }: React.ComponentPropsWithoutRef<'div'> & { priority?: boolean }) { return ( -
-
-
- {children} -
- +
+
+ {children} +
) } From 5e34e6826f1149d1fbf0b6c79b70cfbb609cb991 Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Mon, 13 Oct 2025 16:10:09 +0200 Subject: [PATCH 03/21] feat: wrap homepage sections in AnimatedSection components for scroll animations --- src/app/(main)/page.tsx | 34 ++++++++++++++++++++++-------- src/components/AnimatedSection.tsx | 23 ++++++++++++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 src/components/AnimatedSection.tsx diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx index 7ab1cbb..5002a15 100644 --- a/src/app/(main)/page.tsx +++ b/src/app/(main)/page.tsx @@ -1,7 +1,7 @@ +import { AnimatedSection } from '@/components/AnimatedSection' import { CallToAction } from '@/components/CallToAction' import { Faqs } from '@/components/Faqs' import { Hero } from '@/components/Hero' -import { Pricing } from '@/components/Pricing' import { PrimaryFeatures } from '@/components/PrimaryFeatures' import { UseCases } from '@/components/UseCases' import { SecondaryFeatures } from '@/components/SecondaryFeatures' @@ -11,14 +11,30 @@ import { About } from '@/components/About' export default function Home() { return ( <> - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + ) } diff --git a/src/components/AnimatedSection.tsx b/src/components/AnimatedSection.tsx new file mode 100644 index 0000000..a1402e0 --- /dev/null +++ b/src/components/AnimatedSection.tsx @@ -0,0 +1,23 @@ +'use client' + +import { useRef } from 'react' +import { motion, useInView } from 'framer-motion' + +export function AnimatedSection({ children }: { children: React.ReactNode }) { + const ref = useRef(null) + const isInView = useInView(ref, { once: false, margin: '-50% 0px -50% 0px' }) + + return ( + + {children} + + ) +} From 8b7f5f728607b890f26dcc9d8331991f6ce8ee4d Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Mon, 13 Oct 2025 16:49:00 +0200 Subject: [PATCH 04/21] refactor: replace Benefits component with Features and remove featured section from Hero --- src/app/(main)/page.tsx | 3 +- src/components/Features.tsx | 126 ++++++++++++++++++ src/components/Hero.tsx | 4 +- src/components/Pathfinding.tsx | 233 +++++++++++++++++++++++++++++++++ 4 files changed, 363 insertions(+), 3 deletions(-) create mode 100644 src/components/Features.tsx create mode 100644 src/components/Pathfinding.tsx diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx index 5002a15..6e175fe 100644 --- a/src/app/(main)/page.tsx +++ b/src/app/(main)/page.tsx @@ -7,6 +7,7 @@ import { UseCases } from '@/components/UseCases' import { SecondaryFeatures } from '@/components/SecondaryFeatures' import { Benefits } from '@/components/Benefits' import { About } from '@/components/About' +import { Features } from '@/components/Features' export default function Home() { return ( @@ -18,7 +19,7 @@ export default function Home() { - + diff --git a/src/components/Features.tsx b/src/components/Features.tsx new file mode 100644 index 0000000..c29d33b --- /dev/null +++ b/src/components/Features.tsx @@ -0,0 +1,126 @@ +import Pathfinding from '@/components/Pathfinding' + +export function Features() { + return ( +
+
+

+ Network Capabilities +

+

+ If you have anything else you want to ask,{' '} + + reach out to us + + . +

+
+
+
+
+ +
+

Routing

+

+ Automatic pathfinding +

+

+ The Mycelium Network automatically discovers the shortest and fastest routes between nodes, + ensuring optimal data flow and network efficiency without manual configuration. +

+
+
+
+
+
+
+
+ +
+

Communication

+

+ Distributed message bus +

+

+ Acts as a global message layer that lets nodes exchange information seamlessly. + Enables resilient, asynchronous communication across the entire decentralized mesh. +

+
+
+
+
+
+
+
+ +
+

Discovery

+

+ Automatic proxy detection +

+

+ The system continuously scans for open SOCKS5 proxies within the network, + making it effortless to find available connection points without manual setup. +

+
+
+
+
+
+
+
+ +
+

Connectivity

+

+ Seamless proxy forwarding +

+

+ 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. +

+
+
+
+
+
+
+
+ +
+

Delivery

+

+ Decentralized content distribution +

+

+ 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. +

+
+
+
+
+
+
+
+ ) +} diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index 1ba18a2..caa136a 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -138,7 +138,7 @@ export function Hero() { />
-
+ {/*

As featured in

@@ -161,7 +161,7 @@ export function Hero() { ))} -
+
*/}
diff --git a/src/components/Pathfinding.tsx b/src/components/Pathfinding.tsx new file mode 100644 index 0000000..718c240 --- /dev/null +++ b/src/components/Pathfinding.tsx @@ -0,0 +1,233 @@ +// pathfinding.tsx +// Animated SVG illustrating "Automatic pathfinding" +// - Central hub + surrounding nodes +// - Arrows fade/slide in +// - Shortest path highlights on loop +// - Respects prefers-reduced-motion +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; +import clsx from 'clsx'; + + +type Props = { + className?: string; // e.g. "w-full h-64" + accent?: string; // main accent color + stroke?: string; // neutral stroke color + bg?: string; // background color +}; + +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 ( + <> + {/* outer ring */} + + {/* core node */} + + + ); +}; + +const Arrow = ({ + d, + color = "#111827", + delay = 0, +}: { + d: string; + color?: string; + delay?: number; +}) => ( + +); + +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 ( + + ); +}; + +export default function Pathfinding({ + className, + accent = "#00b8db", // indigo-800 vibe + stroke = "#111827", // gray-900 + bg = "#FFFFFF", +}: Props) { + // Canvas + const W = 760; + const H = 420; + + // Layout (simple radial) + const center = { x: 380, y: 210 }; + const nodes = [ + { x: 130, y: 210 }, // left + { x: 670, y: 210 }, // right + { x: 380, y: 70 }, // top + { x: 280, y: 340 }, // bottom-left + { x: 500, y: 340 }, // bottom-right + ]; + + // Helper to make arrow path with a small head + 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 }; // inset a bit + 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}`; + }; + + // "Shortest" highlighted route: left -> center -> bottom-right + 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}`; + + // Faint alternative routes + 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 ( + + ); +} From ce162cd169fc275e131e976b54acaacdcb4abfef Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Mon, 13 Oct 2025 17:39:47 +0200 Subject: [PATCH 05/21] feat: replace static images with MessageBus and ProxyDetection components in Features section --- src/components/Features.tsx | 21 +--- src/components/MessageBus.tsx | 150 ++++++++++++++++++++++ src/components/ProxyDetection.tsx | 194 +++++++++++++++++++++++++++++ src/components/ProxyForwarding.tsx | 175 ++++++++++++++++++++++++++ 4 files changed, 525 insertions(+), 15 deletions(-) create mode 100644 src/components/MessageBus.tsx create mode 100644 src/components/ProxyDetection.tsx create mode 100644 src/components/ProxyForwarding.tsx diff --git a/src/components/Features.tsx b/src/components/Features.tsx index c29d33b..367e4d7 100644 --- a/src/components/Features.tsx +++ b/src/components/Features.tsx @@ -1,4 +1,7 @@ import Pathfinding from '@/components/Pathfinding' +import MessageBus from '@/components/MessageBus' +import ProxyDetection from '@/components/ProxyDetection' +import ProxyForwarding from '@/components/ProxyForwarding' export function Features() { return ( @@ -38,11 +41,7 @@ export function Features() {
- +

Communication

@@ -59,11 +58,7 @@ export function Features() {

- +

Discovery

@@ -101,11 +96,7 @@ export function Features() {

- +

Delivery

diff --git a/src/components/MessageBus.tsx b/src/components/MessageBus.tsx new file mode 100644 index 0000000..b63c4ba --- /dev/null +++ b/src/components/MessageBus.tsx @@ -0,0 +1,150 @@ +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +type Props = { + className?: string; // e.g. "w-full h-72" + bg?: string; // default white +}; + +/** Palette (gray/black + accent only) */ +const ACCENT = '#00b8db'; +const STROKE = '#111827'; // black-ish +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', // 'left1' | 'left2' | 'rightTop' | 'rightBottom' | '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(); + + // simple keyframe paths (straight line segments) + const paths: Record = { + 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 ( + + {/* envelope body */} + + {/* flap */} + + + ); +} + +export default function MessageBus({ className, bg = '#ffffff' }: Props) { + const W = 900; + const H = 460; + + return ( +

+ ); +} diff --git a/src/components/ProxyDetection.tsx b/src/components/ProxyDetection.tsx new file mode 100644 index 0000000..6ddaeb7 --- /dev/null +++ b/src/components/ProxyDetection.tsx @@ -0,0 +1,194 @@ +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +type Props = { + className?: string; // e.g. "w-full h-64" + bg?: string; // defaults to white +}; + +// Palette (only these) +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; // rotate handle direction + delay?: number; + duration?: number; +}) { + const prefersReduced = useReducedMotion(); + + return ( + + {/* glass */} + + {/* subtle scanning pulse inside the glass */} + + {/* handle */} + + + + + + ); +} + +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 ( + + {/* outer box */} + + {/* top bar */} + + {/* activity line */} + + {/* “detected” indicator */} + + + ); +} + +export default function ProxyDetection({ className, bg = '#ffffff' }: Props) { + // Canvas + const W = 900; + const H = 180; + + // Layout: a row of proxy servers + const rowY = H / 2; + const xs = [180, 320, 460, 600, 740]; + + // Sequence timings so boxes light up as magnifier passes + const delays = [0.8, 0.6, 0.4, 0.2, 0.0]; + + return ( + + ); +} diff --git a/src/components/ProxyForwarding.tsx b/src/components/ProxyForwarding.tsx new file mode 100644 index 0000000..a76517e --- /dev/null +++ b/src/components/ProxyForwarding.tsx @@ -0,0 +1,175 @@ +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +type Props = { + className?: string; // e.g. "w-full h-72" + bg?: string; // default white +}; + +/** Palette */ +const ACCENT = '#00b8db'; +const STROKE = '#111827'; // black-ish +const GRAY = '#9CA3AF'; +const GRAY_LT = '#E5E7EB'; + +function Laptop({ x, y }: { x: number; y: number }) { + return ( + + {/* screen */} + + + {/* base */} + + + ); +} + +function ServerStack({ x, y }: { x: number; y: number }) { + return ( + + {[0, 1, 2].map((i) => ( + + + + + + + + ))} + {/* tiny rack base */} + + + + ); +} + +function Cloud({ x, y }: { x: number; y: number }) { + return ( + + + + + + + + ); +} + +function Arrow({ d, delay = 0 }: { d: string; delay?: number }) { + return ( + + ); +} + +/** Small packet traveling along keyframe x/y arrays */ +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 ( + + ); +} + +export default function ProxyForwarding({ className, bg = '#ffffff' }: Props) { + const W = 1000; + const H = 420; + + // Key points + 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 ( + + ); +} From b320f3e506349bfeab87dd6ee17795cfc410959f Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Mon, 13 Oct 2025 17:53:35 +0200 Subject: [PATCH 06/21] feat: replace placeholder image with ProxyForwarding component and add ContentDistribution section --- src/components/ContentDistribution.tsx | 182 +++++++++++++++++++++++++ src/components/Features.tsx | 9 +- 2 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 src/components/ContentDistribution.tsx diff --git a/src/components/ContentDistribution.tsx b/src/components/ContentDistribution.tsx new file mode 100644 index 0000000..c45af40 --- /dev/null +++ b/src/components/ContentDistribution.tsx @@ -0,0 +1,182 @@ +'use client'; + +import * as React from 'react'; +import { motion, useReducedMotion } from 'framer-motion'; + +type Props = { + className?: string; // e.g. "w-full h-80" + bg?: string; // default white +}; + +/** Palette */ +const ACCENT = '#00b8db'; +const STROKE = '#111827'; +const GRAY = '#9CA3AF'; +const GRAY_LT = '#E5E7EB'; + +/* ---------- small generic icons (no brands) ---------- */ +const IconSquare = () => ( + +); +const IconTriangle = () => ( + +); +const IconHex = () => ( + +); +const IconBolt = () => ( + +); +const IconPlay = () => ( + +); +const IconDB = () => ( + <> + + + + +); + +/* icon inside white circular badge */ +function Badge({ children }: { children: React.ReactNode }) { + return ( + <> + + {children} + + + + + ); +} + +/* ---------- central cloud ---------- */ +function Cloud({ pulse = true }: { pulse?: boolean }) { + const prefersReduced = useReducedMotion(); + return ( + + + + + + + + {/* subtle accent aura */} + + + ); +} + +/* a small packet line from center to a node */ +function Beam({ + x2, + y2, + delay = 0, +}: { + x2: number; + y2: number; + delay?: number; +}) { + const prefersReduced = useReducedMotion(); + return ( + + ); +} + +export default function ContentDistribution({ className, bg = '#ffffff' }: Props) { + const W = 900; + const H = 560; + + // ring radii + const rings = [110, 190, 270]; + + // positions (angle degrees) for badges on rings + const layout = [ + { r: rings[1], a: -20, icon: }, + { r: rings[2], a: 20, icon: }, + { r: rings[0], a: 155, icon: }, + { r: rings[2], a: -145, icon: }, + { r: rings[1], a: 210, icon: }, + { r: rings[0], a: 60, icon: }, + ]; + + const prefersReduced = useReducedMotion(); + + return ( + + ); +} diff --git a/src/components/Features.tsx b/src/components/Features.tsx index 367e4d7..cc6614e 100644 --- a/src/components/Features.tsx +++ b/src/components/Features.tsx @@ -2,6 +2,7 @@ import Pathfinding from '@/components/Pathfinding' import MessageBus from '@/components/MessageBus' import ProxyDetection from '@/components/ProxyDetection' import ProxyForwarding from '@/components/ProxyForwarding' +import ContentDistribution from '@/components/ContentDistribution' export function Features() { return ( @@ -75,11 +76,7 @@ export function Features() {
- +

Connectivity

@@ -96,7 +93,7 @@ export function Features() {

- +

Delivery

From ad1d11cdf1fde0dff5da9645db3ab6c721725c31 Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Tue, 14 Oct 2025 11:25:07 +0200 Subject: [PATCH 07/21] style: update UI with darker background, remove animations and adjust layout spacing --- hooks/useScrollDirection.ts | 46 ++++++++++++++++++++++++++ src/components/About.tsx | 4 +-- src/components/ContentDistribution.tsx | 15 ++++++--- src/components/LinuxLink.tsx | 2 +- src/components/PrimaryFeatures.tsx | 2 +- 5 files changed, 61 insertions(+), 8 deletions(-) create mode 100644 hooks/useScrollDirection.ts diff --git a/hooks/useScrollDirection.ts b/hooks/useScrollDirection.ts new file mode 100644 index 0000000..c360c5f --- /dev/null +++ b/hooks/useScrollDirection.ts @@ -0,0 +1,46 @@ +'use client'; + +import { useState, useEffect } from 'react'; + +export type ScrollDirection = 'up' | 'down'; + +/** + * A hook to detect the scroll direction. + * It uses requestAnimationFrame for performance, comparing the current scroll position + * with the previous one to determine if the user is scrolling up or down. + * + * @returns {ScrollDirection | null} The current scroll direction ('up' or 'down'), or null on the initial render. + */ +export function useScrollDirection(): ScrollDirection | null { + const [scrollDirection, setScrollDirection] = useState(null); + + useEffect(() => { + let lastScrollY = window.pageYOffset; + let ticking = false; + + const updateScrollDirection = () => { + const scrollY = window.pageYOffset; + + if (Math.abs(scrollY - lastScrollY) < 10) { + ticking = false; + return; + } + setScrollDirection(scrollY > lastScrollY ? 'down' : 'up'); + lastScrollY = scrollY > 0 ? scrollY : 0; + ticking = false; + }; + + const onScroll = () => { + if (!ticking) { + window.requestAnimationFrame(updateScrollDirection); + ticking = true; + } + }; + + window.addEventListener('scroll', onScroll); + + return () => window.removeEventListener('scroll', onScroll); + }, []); + + return scrollDirection; +} \ No newline at end of file diff --git a/src/components/About.tsx b/src/components/About.tsx index 8641491..d9595fd 100644 --- a/src/components/About.tsx +++ b/src/components/About.tsx @@ -6,10 +6,10 @@ export function About() { return (

- +
diff --git a/src/components/ContentDistribution.tsx b/src/components/ContentDistribution.tsx index c45af40..af24c64 100644 --- a/src/components/ContentDistribution.tsx +++ b/src/components/ContentDistribution.tsx @@ -153,24 +153,31 @@ export default function ContentDistribution({ className, bg = '#ffffff' }: Props {/* central cloud */} - {/* rotating layer with badges */} + {/* rotating layer with badges and beams */} + {/* Beams */} + {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 ; + })} + + {/* Badges */} {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 ( - + {n.icon} - {/* beam from center to this node (animated) */} - ); })} diff --git a/src/components/LinuxLink.tsx b/src/components/LinuxLink.tsx index ede0070..1e0f251 100644 --- a/src/components/LinuxLink.tsx +++ b/src/components/LinuxLink.tsx @@ -8,7 +8,7 @@ export function LinuxLink({ }) { return ( Date: Tue, 14 Oct 2025 12:01:33 +0200 Subject: [PATCH 08/21] refactor: update UI styling and download link to point to GitHub releases --- src/app/(auth)/login/page.tsx | 49 --------------------- src/app/(auth)/register/page.tsx | 75 -------------------------------- src/app/(main)/download/page.tsx | 12 +++++ src/components/About.tsx | 4 +- src/components/DownloadLink.tsx | 4 +- src/components/Header.tsx | 2 +- src/components/Hero.tsx | 2 +- 7 files changed, 19 insertions(+), 129 deletions(-) delete mode 100644 src/app/(auth)/login/page.tsx delete mode 100644 src/app/(auth)/register/page.tsx create mode 100644 src/app/(main)/download/page.tsx diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx deleted file mode 100644 index 013dbbd..0000000 --- a/src/app/(auth)/login/page.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { type Metadata } from 'next' -import Link from 'next/link' - -import { AuthLayout } from '@/components/AuthLayout' -import { Button } from '@/components/Button' -import { TextField } from '@/components/Fields' - -export const metadata: Metadata = { - title: 'Sign In', -} - -export default function Login() { - return ( - - Don’t have an account?{' '} - - Sign up - {' '} - for a free trial. - - } - > -
-
- - -
- -
-
- ) -} diff --git a/src/app/(auth)/register/page.tsx b/src/app/(auth)/register/page.tsx deleted file mode 100644 index 1434f9b..0000000 --- a/src/app/(auth)/register/page.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { type Metadata } from 'next' -import Link from 'next/link' - -import { AuthLayout } from '@/components/AuthLayout' -import { Button } from '@/components/Button' -import { SelectField, TextField } from '@/components/Fields' - -export const metadata: Metadata = { - title: 'Sign Up', -} - -export default function Register() { - return ( - - Already registered?{' '} - - Sign in - {' '} - to your account. - - } - > -
-
- - - - - - - - - - -
- -
-
- ) -} diff --git a/src/app/(main)/download/page.tsx b/src/app/(main)/download/page.tsx new file mode 100644 index 0000000..3e3373f --- /dev/null +++ b/src/app/(main)/download/page.tsx @@ -0,0 +1,12 @@ +import { AnimatedSection } from '@/components/AnimatedSection' +import { Hero } from '@/components/Hero' + +export default function Download() { + return ( + <> + + + + + ) +} diff --git a/src/components/About.tsx b/src/components/About.tsx index d9595fd..8641491 100644 --- a/src/components/About.tsx +++ b/src/components/About.tsx @@ -6,10 +6,10 @@ export function About() { return (
- +
diff --git a/src/components/DownloadLink.tsx b/src/components/DownloadLink.tsx index 07938e7..ebaa618 100644 --- a/src/components/DownloadLink.tsx +++ b/src/components/DownloadLink.tsx @@ -4,8 +4,10 @@ import { ArrowDownTrayIcon } from '@heroicons/react/24/solid' export function DownloadLink() { return ( diff --git a/src/components/Header.tsx b/src/components/Header.tsx index d85b851..34a7ff8 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -126,7 +126,7 @@ export function Header() { - +
diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx index caa136a..fe54ea0 100644 --- a/src/components/Hero.tsx +++ b/src/components/Hero.tsx @@ -98,7 +98,7 @@ function PlayIcon(props: React.ComponentPropsWithoutRef<'svg'>) { export function Hero() { return ( -
+
From 8f860926bbb7912a97dccfe6fcb4e7add25df753 Mon Sep 17 00:00:00 2001 From: sasha-astiadi Date: Tue, 14 Oct 2025 12:41:18 +0200 Subject: [PATCH 09/21] refactor: reorganize landing page sections and add hover animations to feature cards --- src/app/(main)/download/page.tsx | 4 +- src/app/(main)/page.tsx | 5 +- src/components/About.tsx | 3 -- src/components/DownloadHero.tsx | 77 ++++++++++++++++++++++++++++ src/components/Features.tsx | 12 ++--- src/components/Footer.tsx | 31 ++++------- src/components/Header.tsx | 2 +- src/components/PrimaryFeatures.tsx | 4 +- src/components/SecondaryFeatures.tsx | 2 +- src/images/android.svg | 2 +- src/images/apple.svg | 1 + src/images/github.svg | 4 ++ src/images/linux.svg | 4 +- src/images/windows.svg | 1 + 14 files changed, 107 insertions(+), 45 deletions(-) create mode 100644 src/components/DownloadHero.tsx create mode 100644 src/images/apple.svg create mode 100644 src/images/github.svg create mode 100644 src/images/windows.svg diff --git a/src/app/(main)/download/page.tsx b/src/app/(main)/download/page.tsx index 3e3373f..dc253a5 100644 --- a/src/app/(main)/download/page.tsx +++ b/src/app/(main)/download/page.tsx @@ -1,11 +1,11 @@ import { AnimatedSection } from '@/components/AnimatedSection' -import { Hero } from '@/components/Hero' +import DownloadHero from '@/components/DownloadHero' export default function Download() { return ( <> - + ) diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx index 6e175fe..db00e55 100644 --- a/src/app/(main)/page.tsx +++ b/src/app/(main)/page.tsx @@ -25,14 +25,11 @@ export default function Home() { - + - - - diff --git a/src/components/About.tsx b/src/components/About.tsx index 8641491..7c280fc 100644 --- a/src/components/About.tsx +++ b/src/components/About.tsx @@ -22,9 +22,6 @@ export function About() {

Our mission is to create a sustainable digital ecosystem where communication is seamless, data is secure, and scalability knows no bounds.

-
- -
diff --git a/src/components/DownloadHero.tsx b/src/components/DownloadHero.tsx new file mode 100644 index 0000000..eaf5810 --- /dev/null +++ b/src/components/DownloadHero.tsx @@ -0,0 +1,77 @@ +import Image from 'next/image'; +import appleIcon from '@/images/apple.svg'; +import windowsIcon from '@/images/windows.svg'; +import androidIcon from '@/images/android.svg'; +import linuxIcon from '@/images/linux.svg'; + +const features = [ + { + name: 'Download for iOS & MacOS', + description: 'Download Mycelium App from the Apple Store.', + href: 'https://apps.apple.com/us/app/mycelium-network/id6504277565', + icon: appleIcon, + }, + { + name: 'Download for Windows', + description: 'Download the Mycelium App for Windows directly from its Github repository.', + href: 'https://github.com/threefoldtech/myceliumflut/releases', + icon: windowsIcon, + }, + { + name: 'Download for Android', + description: 'Download Mycelium from the Google Play Store.', + href: 'https://play.google.com/store/apps/details?id=tech.threefold.mycelium&pli=1', + icon: androidIcon, + }, + { + name: 'Download for Linux', + description: 'Download the Mycelium binary for Linux directly from its Github repository.', + href: 'https://github.com/threefoldtech/mycelium/releases/tag/v0.6.1', + icon: linuxIcon, + }, +]; + +export default function DownloadHero() { + return ( +
+
+
+

+ Download Mycelium +

+

+ Get Mycelium for Android, Windows, macOS, and iOS to securely connect, store, and interact with the decentralized network—seamlessly and efficiently. Not sure how it works?{' '} + + Read the manual. + +

+
+
+
+ {features.map((feature) => ( +
+
+
+ +
+ {feature.name} +
+
+

{feature.description}

+

+ + Download Now + +

+
+
+ ))} +
+
+
+
+ ); +} diff --git a/src/components/Features.tsx b/src/components/Features.tsx index cc6614e..c43179a 100644 --- a/src/components/Features.tsx +++ b/src/components/Features.tsx @@ -21,8 +21,8 @@ export function Features() { .

-
-
+
+
@@ -39,7 +39,7 @@ export function Features() {
-
+
@@ -56,7 +56,7 @@ export function Features() {
-
+
@@ -73,7 +73,7 @@ export function Features() {
-
+
@@ -90,7 +90,7 @@ export function Features() {
-
+
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index d8587c3..9ee891d 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -5,28 +5,16 @@ import { Button } from '@/components/Button' import { Container } from '@/components/Container' import { TextField } from '@/components/Fields' import { NavLinks } from '@/components/NavLinks' -import qrCode from '@/images/qr-code.svg' - -function QrCodeBorder(props: React.ComponentPropsWithoutRef<'svg'>) { - return ( - - ) -} +import github from '@/images/github.svg' export function Footer() { return (