import React, { useState, useEffect, useRef } from "react"; import Link from "next/link"; const asciiStyle: React.CSSProperties = { fontFamily: "var(--font-mono)", fontSize: "0.72rem", lineHeight: 1.4, color: "var(--colorPrimary)", opacity: 0.7, whiteSpace: "pre", margin: 0, }; const randomRow = () => Math.floor(Math.random() * 3); const randomPath = () => [randomRow(), randomRow(), randomRow()]; const NetworkAnimation = () => { // Packets traverse 5 stages: gw_e(0) → M1(1) → M2(2) → M3(3) → gw_ex(4) // stage -1 = not yet mounted (SSR-safe, renders all ○) const [packets, setPackets] = useState([ { path: randomPath(), stage: -1 }, { path: randomPath(), stage: -1 }, ]); useEffect(() => { // kick off with staggered positions setPackets([ { path: randomPath(), stage: 0 }, { path: randomPath(), stage: 3 }, ]); const id = setInterval(() => { setPackets((prev) => prev.map((p) => { const next = (p.stage + 1) % 5; return { stage: next, path: next === 0 ? randomPath() : p.path }; }) ); }, 300); return () => clearInterval(id); }, []); const gwNode = (stage: number) => { const active = packets.some((p) => p.stage === stage); return ( {active ? "\u25CF" : "\u25CB"} ); }; const mixNode = (col: number, row: number) => { const active = packets.some( (p) => p.stage === col + 1 && p.path[col] === row ); const filled = active; return ( {filled ? "\u25CF" : "\u25CB"} ); }; return (
      {"gw_e  M1   M2   M3  gw_ex\n"}
      {"       "}
      {mixNode(0, 0)}
      {" \u2500\u2500 "}
      {mixNode(1, 0)}
      {" \u2500\u2500 "}
      {mixNode(2, 0)}
      {"\n"}
      {"        \\  / \\  /\n"}
      {"  "}
      {gwNode(0)}
      {" \u2500\u2500 "}
      {mixNode(0, 1)}
      {" \u2500\u2500 "}
      {mixNode(1, 1)}
      {" \u2500\u2500 "}
      {mixNode(2, 1)}
      {" \u2500\u2500 "}
      {gwNode(4)}
      {"\n"}
      {"        /  \\ /  \\\n"}
      {"       "}
      {mixNode(0, 2)}
      {" \u2500\u2500 "}
      {mixNode(1, 2)}
      {" \u2500\u2500 "}
      {mixNode(2, 2)}
    
); }; const TypewriterAnimation = () => { const text = "let client = MixnetClient::connect_new().await?;\n" + "\n" + "client.send(msg).await;"; const [charCount, setCharCount] = useState(0); const [showCursor, setShowCursor] = useState(true); useEffect(() => { let cancelled = false; const run = () => { setCharCount(0); let i = 0; const type = () => { if (cancelled) return; if (i <= text.length) { setCharCount(i); i++; setTimeout(type, 40); } else { setTimeout(() => { if (!cancelled) run(); }, 2000); } }; type(); }; run(); return () => { cancelled = true; }; }, []); useEffect(() => { const id = setInterval(() => setShowCursor((v) => !v), 530); return () => clearInterval(id); }, []); return (
      {text.slice(0, charCount)}
      {showCursor ? "\u258C" : " "}
      {text.slice(charCount)}
    
); }; const OperatorsAnimation = () => { const totalBars = 10; const [tick, setTick] = useState(0); const mixRef = useRef(0); const [mixCount, setMixCount] = useState(0); useEffect(() => { const id = setInterval(() => { setTick((t) => t + 1); mixRef.current += Math.floor(Math.random() * 8) + 5; setMixCount(mixRef.current); }, 80); return () => clearInterval(id); }, []); const mixFilled = Math.min(tick % 12, totalBars); const bar = (f: number) => "\u25A0".repeat(f) + "\u25A1".repeat(totalBars - f); const fmt = (n: number) => n.toLocaleString("en"); return (
      {"> nym-node run\n\n"}
      {"  mixing: "}
      {bar(mixFilled)}
      {"  "}
      {fmt(mixCount)}
      {" pkts"}
    
); }; const ApiAnimation = () => { const lines = [ "GET /v1/mixnodes/active", "", '{ "count": 498,', ' "nodes": [ ... ] }', ]; const [visibleLines, setVisibleLines] = useState(0); useEffect(() => { let cancelled = false; const run = () => { setVisibleLines(0); setTimeout(() => { if (cancelled) return; setVisibleLines(1); setTimeout(() => { if (cancelled) return; let i = 2; const reveal = () => { if (cancelled) return; if (i <= lines.length) { setVisibleLines(i); i++; setTimeout(reveal, 300); } else { setTimeout(() => { if (!cancelled) run(); }, 2000); } }; reveal(); }, 800); }, 100); }; run(); return () => { cancelled = true; }; }, []); return (
      {lines.slice(0, visibleLines).map((line, i) => (
        
          {i > 0 && "\n"}
          {line}
        
      ))}
      
        {lines.slice(visibleLines).map((line, i) => (
          
            {visibleLines > 0 || i > 0 ? "\n" : ""}
            {line}
          
        ))}
      
    
); }; const sections = [ { title: "Network", description: "Architecture, cryptographic systems, and how the Mixnet protects your traffic.", href: "/network", animation: "network" as const, }, { title: "Developers", description: "SDKs, tutorials, and integration guides for building on Nym.", href: "/developers", animation: "typewriter" as const, }, { title: "Operators", description: "Set up and maintain mix nodes, gateways, and network infrastructure.", href: "/operators/introduction", animation: "progress" as const, }, { title: "APIs", description: "Interactive specs for querying Nym infrastructure.", href: "/apis/introduction", animation: "api" as const, }, ]; const AnimationBlock = ({ type }: { type: string }) => { switch (type) { case "network": return ; case "typewriter": return ; case "progress": return ; case "api": return ; default: return null; } }; const sdks = [ { name: "Rust SDK", description: "Async Mixnet client with AsyncRead/AsyncWrite streams over the Mixnet.", href: "/developers/rust", }, { name: "smolmix", description: "TCP/UDP tunnel over the Mixnet. Userspace smoltcp stack exposing AsyncRead/AsyncWrite TcpStream and UdpSocket types.", href: "/developers/smolmix", }, { name: "TypeScript SDK", description: "Browser-side Mixnet Client for raw messaging via WebSocket, plus Nyx smart contract bindings.", href: "/developers/typescript", }, { name: "mix-fetch", description: "fetch()-compatible API that routes HTTP(S) requests through the Mixnet. Browsers and Node.js.", href: "/developers/mix-fetch", }, { name: "mix-tunnel", description: "Owns the shared Mixnet tunnel that mix-fetch, mix-dns, and mix-websocket ride on. One IPR connection and userspace TCP/IP stack for all three.", href: "/developers/mix-tunnel", }, { name: "mix-dns", description: "Resolves hostnames to IPs through the Mixnet. UDP DNS via an IPR exit, no TCP or TLS.", href: "/developers/mix-dns", }, { name: "mix-websocket", description: "WebSocket-compatible class for ws and wss traffic routed through the Mixnet via an IPR exit.", href: "/developers/mix-websocket", }, ]; export const LandingPage = () => { return (
{sections.map((s, i) => (

{s.title}

{s.description}

))}

Libraries

Rust and TypeScript libraries for Mixnet integration.

{sdks.map((sdk, i) => (
0 ? "-1px" : undefined, display: "flex", alignItems: "center", justifyContent: "space-between", transition: "background-color 0.15s", cursor: "pointer", }} >
{sdk.name}

{sdk.description}

))}
); };