diff --git a/docs/app/DevconBanner.tsx b/docs/app/DevconBanner.tsx index f6533e34fd..a034c4a100 100644 --- a/docs/app/DevconBanner.tsx +++ b/docs/app/DevconBanner.tsx @@ -3,7 +3,7 @@ export function DevconBanner() { if (Date.now() > 1731603600000) return null; return ( -
+
Hello Devcon! Come learn about MUD at{" "} MUD Day diff --git a/docs/app/api/contributors/getContributors.ts b/docs/app/api/contributors/getContributors.ts new file mode 100644 index 0000000000..785c1ceb59 --- /dev/null +++ b/docs/app/api/contributors/getContributors.ts @@ -0,0 +1,17 @@ +import { headers } from "next/headers"; + +export async function getContributors() { + const headersList = headers(); + const host = headersList.get("host") || ""; + const protocol = headersList.get("x-forwarded-proto") || "http"; + const baseUrl = `${protocol}://${host}`; + + try { + const response = await fetch(`${baseUrl}/api/contributors`); + const data = await response.json(); + return data; + } catch (error) { + console.error(error); + return []; + } +} diff --git a/docs/app/api/contributors/route.ts b/docs/app/api/contributors/route.ts new file mode 100644 index 0000000000..d9eec44c93 --- /dev/null +++ b/docs/app/api/contributors/route.ts @@ -0,0 +1,44 @@ +import { Octokit } from "octokit"; + +const octokit = new Octokit(); + +export async function GET() { + try { + const response = await octokit.request("GET /repos/{owner}/{repo}/contributors", { + owner: "latticexyz", + repo: "mud", + }); + + const allContributors = response.data; + if (!Array.isArray(allContributors)) { + return Response.json({ + count: 0, + contributors: [], + }); + } + + const userContributors = allContributors + ?.filter((contributor: { type: string }) => contributor.type === "User") + .map((contributor) => { + const weightedContributions = Math.log(contributor.contributions + 1) * 10; + const index = Math.random() * weightedContributions; + const score = weightedContributions - index; + return { + ...contributor, + score, + }; + }) + .sort((a, b) => b.score - a.score); + + return Response.json({ + count: userContributors.length, + contributors: userContributors?.slice(0, 7), + }); + } catch (error) { + console.error(error); + return Response.json({ + count: 0, + contributors: [], + }); + } +} diff --git a/docs/app/api/stargazers/getStargazers.ts b/docs/app/api/stargazers/getStargazers.ts new file mode 100644 index 0000000000..c8712d2a34 --- /dev/null +++ b/docs/app/api/stargazers/getStargazers.ts @@ -0,0 +1,17 @@ +import { headers } from "next/headers"; + +export async function getStargazers() { + const headersList = headers(); + const host = headersList.get("host") || ""; + const protocol = headersList.get("x-forwarded-proto") || "http"; + const baseUrl = `${protocol}://${host}`; + + try { + const response = await fetch(`${baseUrl}/api/stargazers`); + const data = await response.json(); + return data; + } catch (error) { + console.error(error); + return 0; + } +} diff --git a/docs/app/api/stargazers/route.ts b/docs/app/api/stargazers/route.ts new file mode 100644 index 0000000000..41285a2399 --- /dev/null +++ b/docs/app/api/stargazers/route.ts @@ -0,0 +1,19 @@ +import { Octokit } from "octokit"; + +const octokit = new Octokit(); + +export async function GET() { + try { + const { + data: { stargazers_count: totalStars }, + } = await octokit.request("GET /repos/{owner}/{repo}", { + owner: "latticexyz", + repo: "mud", + }); + + return Response.json(totalStars); + } catch (error) { + console.error(error); + return Response.json(0); + } +} diff --git a/docs/app/globals.css b/docs/app/globals.css new file mode 100644 index 0000000000..210babb0b4 --- /dev/null +++ b/docs/app/globals.css @@ -0,0 +1,88 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Hide scrollbar for Chrome, Safari and Opera */ +body::-webkit-scrollbar, +div::-webkit-scrollbar, +pre::-webkit-scrollbar { + display: none; +} + +body, +div, +pre { + -ms-overflow-style: none; + scrollbar-width: none; +} + +html { + background-color: black; +} + +pre { + font-size: 0.5; + background-color: #222; + border-color: #222; + border-width: 1rem; + border-radius: 0.4rem; + overflow-x: scroll; + margin-bottom: 18px; +} + +summary { + margin-bottom: 16px; +} + +summary > p { + display: inline; +} + +/* Substack subscribe widget */ +#custom-substack-embed { + max-width: 530px !important; + flex-direction: column !important; +} + +#custom-substack-embed p { + position: absolute !important; + max-width: 530px !important; + font-family: var(--font-supply-mono) !important; + text-transform: uppercase !important; +} + +.custom-substack-widget { + flex-wrap: nowrap !important; + max-width: 600px !important; + border: none !important; + border-radius: 0 !important; +} + +.custom-substack-widget input { + flex-basis: 100% !important; + width: 100% !important; + padding-left: 20px !important; + padding-right: 20px !important; + font-size: 20px !important; + font-family: var(--font-supply-mono) !important; + text-transform: uppercase !important; + border: 1px solid rgba(255, 255, 255, 0.2) !important; + box-sizing: border-box !important; +} + +.custom-substack-widget input::placeholder { + opacity: 0.3 !important; +} + +.custom-substack-widget button { + flex-shrink: 0 !important; + flex-grow: 0 !important; + width: 122px !important; + /* flex-basis: 122px !important; */ + border: none !important; + padding: 23px 25px !important; + margin-left: 15px !important; + font-size: 20px !important; + font-family: var(--font-supply-mono) !important; + text-transform: uppercase !important; +} diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx index 19b6ce56b1..abbc20228e 100644 --- a/docs/app/layout.tsx +++ b/docs/app/layout.tsx @@ -1,10 +1,30 @@ -import "tailwindcss/tailwind.css"; - import { Metadata } from "next"; import { ReactNode } from "react"; -import { twMerge } from "tailwind-merge"; import localFont from "next/font/local"; +import { cn } from "../lib/cn"; import { DevconBanner } from "./DevconBanner"; +import "./globals.css"; + +const basierCircle = localFont({ + src: [ + { + path: "../public/fonts/BasierCircle-Regular.otf", + weight: "400", + style: "normal", + }, + { + path: "../public/fonts/BasierCircle-SemiBold.otf", + weight: "600", + style: "normal", + }, + { + path: "../public/fonts/BasierCircle-Bold.otf", + weight: "700", + style: "normal", + }, + ], + variable: "--font-basier-circle", +}); const supplyMono = localFont({ src: "../public/fonts/PPSupplyMono-Regular.woff2", @@ -22,6 +42,13 @@ const supplyMono = localFont({ ], }); +const berkeleyMono = localFont({ + src: "../public/fonts/BerkeleyMono-Regular.otf", + preload: true, + variable: "--font-berkeley-mono", + fallback: ["ui-monospace"], +}); + export const metadata: Metadata = { title: "MUD | Framework for onchain applications", description: @@ -38,7 +65,14 @@ type Props = { children: ReactNode }; export default function Layout({ children }: Props) { return ( - + {children} diff --git a/docs/app/page.tsx b/docs/app/page.tsx index d8dfaa034a..aff2a0b69a 100644 --- a/docs/app/page.tsx +++ b/docs/app/page.tsx @@ -1,13 +1,14 @@ -import { LatticeIcon } from "../src/icons/LatticeIcon"; import { Metadata } from "next"; -import Link from "next/link"; -import { twMerge } from "tailwind-merge"; -import { SourceIcon } from "../src/icons/SourceIcon"; -import { DocsIcon } from "../src/icons/DocsIcon"; -import { StatusIcon } from "../src/icons/StatusIcon"; -import { CalendarIcon } from "../src/icons/CalendarIcon"; -import { ContributeIcon } from "../src/icons/ContributeIcon"; -import { ChangelogIcon } from "../src/icons/ChangelogIcon"; +import Hero from "./sections/Hero"; +import FindUs from "./sections/FindUs"; +import Resources from "./sections/Resources"; +import TrustedBy from "./sections/TrustedBy/TrustedBy"; +import Installation from "./sections/Installation"; +import Architecture from "./sections/Architecture"; +import Integrations from "./sections/Integrations"; +import Ecosystem from "./sections/Ecosystem/Ecosystem"; +import Changelog from "./sections/Changelog"; +import Newsletter from "./sections/Newsletter"; export const metadata: Metadata = { title: "MUD | Framework for onchain applications", @@ -20,266 +21,16 @@ export const metadata: Metadata = { export default async function HomePage() { return ( <> -
-
-
-
MUD
-
- - Built by - Lattice - - - -
-
-
- -
-
-
-
- Battle-tested onchain framework for developers. -
-

- MUD provides you with the tools to build ambitious onchain applications. -

-
-
- - Documentation - - - Source - -
-
-
-
-
- -
-
-
-
Resources
-

- Discover more about the open source framework powering complex games & apps on Ethereum. -

-
-
- - - Source code - - - - Changelog - - - - Documentation - - - - Contribute - - - - Status - - - - Roadmap - -
-
- -
-
-
Projects
-

Start using a wide ecosystem of projects powered by MUD.

-
-
- - - Sky Strife - An onchain strategy game - - - - - - OPCraft - Voxel crafting game on OP Stack - - - - - - Primodium - An onchain city-building game - - - - - - Words3 - Onchain scrabble - - - -
-
- -
-
-
Find us
-

Discover more MUD resources, and join our community online.

-
-
- - Newsletter - - - Discord - - - Twitter - - - YouTube - - - Reach out - -
-
-
+ + + + + + + + + + ); } diff --git a/docs/app/sections/Architecture.tsx b/docs/app/sections/Architecture.tsx new file mode 100644 index 0000000000..65fb2a87b6 --- /dev/null +++ b/docs/app/sections/Architecture.tsx @@ -0,0 +1,33 @@ +import Image from "next/image"; +import { Container } from "../../components/ui/Container"; +import { Section } from "../../components/ui/Section"; + +export default function Architecture() { + return ( +
+ +
+
+

Tried and tested

+

+ First released in 2022, MUD has been used by countless onchain developers—from solo devs to 50-strong game + studios—to build applications in production. +

+

+ The MUD automatic indexer, MUD-native account abstraction, support for token standards… all of MUD has + been tried and tested by production worlds with millions of onchain entities and thousands of users. +

+

+ And it is still continuously evolving. New modules and updates are constantly being worked on by MUD core + devs and contributors, bringing new features and performance improvements. +

+
+ +
+ MUD Architecture +
+
+
+
+ ); +} diff --git a/docs/app/sections/Changelog.tsx b/docs/app/sections/Changelog.tsx new file mode 100644 index 0000000000..31cddb0d0d --- /dev/null +++ b/docs/app/sections/Changelog.tsx @@ -0,0 +1,88 @@ +import Link from "next/link"; +import Image from "next/image"; +import { Container } from "../../components/ui/Container"; +import { Section } from "../../components/ui/Section"; +import changelog from "../../data/changelog.json"; + +type ChangelogItem = (typeof changelog)[number]; + +const ChangelogItem = ({ version, date, changes }: ChangelogItem) => { + const allChanges = [...changes.patch, ...changes.minor, ...changes.major]; + if (allChanges.length === 0) { + return null; + } + + return ( + +
+
+
+ {version} +
+

+ {new Date(date).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + })} +

+
+ +
    + {allChanges.slice(0, 3).map((change, index) => ( +
  • + {change.title} +
  • + ))} + + {allChanges.length > 3 && ( +
  • + and {allChanges.length - 3} more... +
  • + )} +
+
+ + ); +}; + +export default function Changelog() { + return ( +
+ +
+
+
+

Changelog

+

Learn what’s changed in recent releases of MUD.

+
+ + + GitHub + Github + +
+
+
+ +
+ +
+ {changelog.map((item, index) => ( + + ))} +
+
+
+
+ ); +} diff --git a/docs/app/sections/Ecosystem/Ecosystem.tsx b/docs/app/sections/Ecosystem/Ecosystem.tsx new file mode 100644 index 0000000000..1b2de3bbe1 --- /dev/null +++ b/docs/app/sections/Ecosystem/Ecosystem.tsx @@ -0,0 +1,67 @@ +"use client"; + +import Image from "next/image"; +import { Section } from "../../../components/ui/Section"; +import { Container } from "../../../components/ui/Container"; +import { cn } from "../../../lib/cn"; +import { projects } from "./projects"; + +export default function Ecosystem() { + return ( +
+ +
+

Ecosystem

+

Start using a wide ecosystem of projects powered by MUD.

+
+ +
+ {projects?.map((project, index) => { + return ( + + {project.name} + +
+
+
+ {`${project.name} +

{project.name}

+
+
+
+
+ ); + })} +
+
+
+ ); +} diff --git a/docs/app/sections/Ecosystem/projects.ts b/docs/app/sections/Ecosystem/projects.ts new file mode 100644 index 0000000000..cc337bf8e2 --- /dev/null +++ b/docs/app/sections/Ecosystem/projects.ts @@ -0,0 +1,98 @@ +export const projects = [ + { + name: "Biomes", + icon: "/ecosystem/icons/biomes.png", + bgImage: "/ecosystem/bg/everlon.png", + url: "https://biomes.aw", + }, + { + name: "EVE Frontier", + icon: "/ecosystem/icons/eve-frontier.jpg", + bgImage: "/ecosystem/bg/eve-frontier.jpg", + url: "https://evefrontier.com/en", + }, + { + name: "CafeCosmos", + icon: "/ecosystem/icons/cafe-cosmos.png", + bgImage: "/ecosystem/bg/cafe-cosmos.jpeg", + url: "https://x.com/CafeCosmosHQ", + }, + { + name: "This Cursed Machine", + icon: "/ecosystem/icons/thiscursedmachine.png", + bgImage: "/ecosystem/bg/thiscursedmachine.png", + url: "https://thiscursedmachine.fun/", + }, + { + name: "Battle for Blockchain", + icon: "/ecosystem/icons/battle-blockchain.png", + bgImage: "/ecosystem/bg/battle-blockchain.jpeg", + url: "https://x.com/battleforblock", + }, + { + name: "For the Kingdom", + icon: "/ecosystem/icons/for-kingdom.png", + bgImage: "/ecosystem/bg/for-kingdom.jpeg", + url: "https://x.com/4thekingdom_xyz", + }, + { + name: "Words3", + icon: "/ecosystem/icons/words3.png", + bgImage: "/ecosystem/bg/words3.png", + url: "https://www.words3.xyz/", + }, + { + name: "Primodium", + icon: "/ecosystem/icons/primodium.jpg", + bgImage: "/ecosystem/bg/primodium.png", + url: "https://x.com/primodiumgame", + }, + { + name: "Dappmon", + icon: "/ecosystem/icons/dappmon.jpg", + bgImage: "/ecosystem/bg/dappmon.jpeg", + url: "dappmon.xyz", + }, + { + name: "Project Mirage", + icon: "/ecosystem/icons/project-mirage.jpg", + bgImage: "/ecosystem/bg/project-mirage.jpeg", + url: "https://x.com/mirage_game_", + }, + { + name: "Yonk", + icon: "/ecosystem/icons/yonk.png", + bgImage: "/ecosystem/bg/yonk.png", + url: "https://apps.apple.com/gb/app/yonk/id6478030288", + }, + { + name: "Dear", + icon: "/ecosystem/icons/dear.png", + bgImage: "/ecosystem/bg/dear.png", + url: "https://www.dear.game/", + }, + { + name: "PopCraft", + icon: "/ecosystem/icons/popcraft.jpg", + bgImage: "/ecosystem/bg/popcraft.png", + url: "https://x.com/PopCraftOnChain", + }, + { + name: "Aether Sands", + description: "An onchain open-world survival & resource management game.", + icon: "/ecosystem/icons/aethersands.png", + bgImage: "/ecosystem/bg/aethersands.png", + url: "https://x.com/aether_sands", + mud: true, + redstone: false, + }, + { + name: "Geoweb", + description: "A public good augmented reality network.", + icon: "/ecosystem/icons/geoweb.png", + bgImage: "/ecosystem/bg/geoweb.png", + url: "https://x.com/thegeoweb", + mud: true, + redstone: false, + }, +]; diff --git a/docs/app/sections/EmptySection.tsx b/docs/app/sections/EmptySection.tsx new file mode 100644 index 0000000000..e15a7e0c30 --- /dev/null +++ b/docs/app/sections/EmptySection.tsx @@ -0,0 +1,10 @@ +import { Container } from "../../components/ui/Container"; +import { Section } from "../../components/ui/Section"; + +export default function EmptySection() { + return ( +
+ Content +
+ ); +} diff --git a/docs/app/sections/FindUs.tsx b/docs/app/sections/FindUs.tsx new file mode 100644 index 0000000000..77f62fdbb9 --- /dev/null +++ b/docs/app/sections/FindUs.tsx @@ -0,0 +1,79 @@ +import Image from "next/image"; +import { Container } from "../../components/ui/Container"; +import { Section } from "../../components/ui/Section"; +import { cn } from "../../lib/cn"; + +function FindUsItem({ + title, + href, + icon, + className, +}: { + title: string; + href: string; + icon: React.ReactNode; + className?: string; +}) { + return ( + + {icon} + {title} + + ); +} + +export default function FindUs() { + return ( +
+ +
+
+

Find us

+

+ Discover more MUD resources, and join our community online. +

+
+ +
+ } + className="hover:bg-mud/20 hover:border-mud/30" + /> + } + className="hover:bg-[#9656ce]/20 hover:border-[#9656ce]/30" + /> + } + className="hover:bg-[#1DA1F2]/20 hover:border-[#1DA1F2]/30" + /> + } + className="hover:bg-[#FF0000]/20 hover:border-[#FF0000]/30" + /> +
+
+
+
+ ); +} diff --git a/docs/app/sections/Hero.tsx b/docs/app/sections/Hero.tsx new file mode 100644 index 0000000000..2e0b0f8530 --- /dev/null +++ b/docs/app/sections/Hero.tsx @@ -0,0 +1,76 @@ +import { LatticeIcon } from "../../src/icons/LatticeIcon"; +import Link from "next/link"; +import { cn } from "../../lib/cn"; +import { Section } from "../../components/ui/Section"; +import { Container } from "../../components/ui/Container"; +import Image from "next/image"; + +export default function Hero() { + return ( +
+ +
+
+

MUD

+
+ +
+
+ MUD +
+ +
+
+ + + + + Built by Lattice + + +
+ Open-source engine for autonomous worlds +
+

+ MUD reduces the complexity of building Ethereum apps with a tightly integrated software stack. +

+
+
+ + Documentation + + + GitHub + Source + +
+
+
+
+
+
+ ); +} diff --git a/docs/app/sections/Installation.tsx b/docs/app/sections/Installation.tsx new file mode 100644 index 0000000000..627666deaf --- /dev/null +++ b/docs/app/sections/Installation.tsx @@ -0,0 +1,70 @@ +"use client"; + +import Link from "next/link"; +import { Container } from "../../components/ui/Container"; +import { Section } from "../../components/ui/Section"; +import CopyButton from "../../components/ui/CopyButton"; +import VideoPlayer from "../../components/ui/VideoModal"; +import Image from "next/image"; + +const videoId = "07b2e147a732cb52ffff39165f35a498"; + +export default function Installation() { + return ( +
+ +
+
+
+

Get started

+

+ + > pnpm create mud + + + +

+
+ +

+ MUD powers some of the most complex and fascinating applications onchain. But getting started is as easy + as running pnpm create mud, to create your very own MUD project. +

+ +

+ From there, choose one of the frontend templates—from React to Three.js—to visualize your world. Then, + start building your world and see it come to life with a built-in development server and automatic + contract hot reloading. +

+ + + Curious to learn more? Read the docs → + +
+ +
+ +
+
+
+ Play +
+ + mud video thumbnail +
+ +
+
+ +
+ ); +} diff --git a/docs/app/sections/Integrations.tsx b/docs/app/sections/Integrations.tsx new file mode 100644 index 0000000000..017cee3401 --- /dev/null +++ b/docs/app/sections/Integrations.tsx @@ -0,0 +1,90 @@ +import Image from "next/image"; +import { Container } from "../../components/ui/Container"; +import { Section } from "../../components/ui/Section"; + +export default function Integrations() { + return ( +
+ +
+
+

Integrations

+

Standard World Interface

+
+ + +
+ +

+ Not only is the MUD codebase fully open-source under the MIT license, all of its interfaces with peripheral + services and execution environments—indexers, blockchains, ERC-4337 bundlers—are openly accessible and under + standardization. +

+ +

+ Launch with Lattice’s Quarry environment to get 7ms ultra-low latency and seamless onboarding, or design your + own stack with any EVM blockchain, an open-source ERC-4337 bundler, and the open-source MUD indexer. +

+ + +
+ +
+
+ + Standard World Interface + +

+ The Standard World Interface enables custom-built, low-latency environments like Lattice's Quarry. +

+
+
+

← Scroll to explore →

+
+
+ ); +} diff --git a/docs/app/sections/Newsletter.tsx b/docs/app/sections/Newsletter.tsx new file mode 100644 index 0000000000..1f545a9f27 --- /dev/null +++ b/docs/app/sections/Newsletter.tsx @@ -0,0 +1,51 @@ +"use client"; + +import { Container } from "../../components/ui/Container"; +import { Section } from "../../components/ui/Section"; +import { useEffect } from "react"; + +export default function Newsletter() { + useEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (window as any).CustomSubstackWidget = { + substackUrl: "newsletter.lattice.xyz", + placeholder: "Enter email here...", + buttonText: "Submit", + theme: "custom", + colors: { + primary: "#FF7613", + input: "#313131", + email: "#FFFFFF", + text: "#FFFFFF", + }, + }; + + const script = document.createElement("script"); + script.src = "https://substackapi.com/widget.js"; + script.async = true; + document.body.appendChild(script); + + return () => { + if (script) { + document.body.removeChild(script); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (window as any).CustomSubstackWidget; + } + }; + }, []); + + return ( +
+ +
+
+

Newsletter

+

Sign up to receive regular updates about MUD.

+
+ +
+
+ +
+ ); +} diff --git a/docs/app/sections/Resources.tsx b/docs/app/sections/Resources.tsx new file mode 100644 index 0000000000..0dbd584e4c --- /dev/null +++ b/docs/app/sections/Resources.tsx @@ -0,0 +1,85 @@ +import Link from "next/link"; +import { SourceIcon } from "../../src/icons/SourceIcon"; +import { DocsIcon } from "../../src/icons/DocsIcon"; +import { StatusIcon } from "../../src/icons/StatusIcon"; +import { CalendarIcon } from "../../src/icons/CalendarIcon"; +import { ContributeIcon } from "../../src/icons/ContributeIcon"; +import { ChangelogIcon } from "../../src/icons/ChangelogIcon"; +import { Section } from "../../components/ui/Section"; +import { Container } from "../../components/ui/Container"; + +export default function Resources() { + return ( +
+ +
+
+

Resources

+

+ Discover more about the open source framework powering complex games & apps on Ethereum. +

+
+ +
+ + + Source code + + + + Changelog + + + + Documentation + + + + Contribute + + + + Status + + + + Roadmap + +
+
+
+
+ ); +} diff --git a/docs/app/sections/TrustedBy/Contributors.tsx b/docs/app/sections/TrustedBy/Contributors.tsx new file mode 100644 index 0000000000..c55d412817 --- /dev/null +++ b/docs/app/sections/TrustedBy/Contributors.tsx @@ -0,0 +1,27 @@ +import Image from "next/image"; +import { getContributors } from "../../api/contributors/getContributors"; + +export default async function Contributors() { + const { contributors, count: contributorsCount } = await getContributors(); + return ( +
+
+ {contributors.map((contributor) => ( + frolic + ))} +
+ +

+ {contributorsCount} + contributors +

+
+ ); +} diff --git a/docs/app/sections/TrustedBy/GitHubStars.tsx b/docs/app/sections/TrustedBy/GitHubStars.tsx new file mode 100644 index 0000000000..d5a7ef1c15 --- /dev/null +++ b/docs/app/sections/TrustedBy/GitHubStars.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { getStargazers } from "../../api/stargazers/getStargazers"; + +export async function GitHubStars() { + const stargazersCount = await getStargazers(); + return ( +
+
+
+

{stargazersCount}

+