matrix stuff

Commited by Exverge <exverge@exverge.xyz> on behalf of Zeke
This commit is contained in:
Zeke 2024-04-10 21:27:21 -04:00 committed by Exverge
parent b068ab99f4
commit de20f25c40
No known key found for this signature in database
GPG key ID: 19AAFC0AC6A9B35A
2 changed files with 726 additions and 692 deletions

View file

@ -1,421 +1,445 @@
<script lang="ts"> <script lang="ts">
import "../app.pcss"; import "../app.pcss";
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import Logo from "../components/LogoWithTextHorizontal.svelte"; import Logo from "../components/LogoWithTextHorizontal.svelte";
import { CodeBranchOutline, DiscordSolid, BarsSolid, CloseSolid } from "flowbite-svelte-icons"; import { CodeBranchOutline, DiscordSolid, BarsSolid, CloseSolid } from "flowbite-svelte-icons";
import { browser } from "$app/environment"; import { browser } from "$app/environment";
import { writable } from "svelte/store"; import { writable } from "svelte/store";
import { setContext } from "svelte"; import { setContext } from "svelte";
import type { TransitionConfig } from "svelte/transition"; import type { TransitionConfig } from "svelte/transition";
import type { PageData } from "./$types"; import type { PageData } from "./$types";
import { bounceOut } from "svelte/easing"; import { bounceOut } from "svelte/easing";
import { generateTransition, transition } from "$lib/util/animation"; import { generateTransition, transition } from "$lib/util/animation";
import { reducedMotion } from "$lib/accessibility"; import { reducedMotion } from "$lib/accessibility";
import BackgroundProvider from "$components/BackgroundProvider.svelte"; import BackgroundProvider from "$components/BackgroundProvider.svelte";
import AccountButton from "$components/AccountButton.svelte"; import AccountButton from "$components/AccountButton.svelte";
export let data: PageData; export let data: PageData;
// TODO: refactor // TODO: refactor
const navigator = browser ? window.navigator : { userAgent: "" }; const navigator = browser ? window.navigator : { userAgent: "" };
interface NavItem { interface NavItem {
name: string; name: string;
href: string; href: string;
} title: string;
target: string;
const token = writable(data.tokenCookie || ""); }
function transitionIn(node: HTMLElement, { duration = 360 }: TransitionConfig) { const token = writable(data.tokenCookie || "");
if ($reducedMotion)
return { function transitionIn(node: HTMLElement, { duration = 360 }: TransitionConfig) {
duration: 0, if ($reducedMotion)
}; return {
node = node.querySelector(".content") || node; duration: 0,
const intensity = node.dataset.intensityIn || "160"; };
const UA = navigator.userAgent; node = node.querySelector(".content") || node;
const ff = UA.indexOf("Firefox") > -1; const intensity = node.dataset.intensityIn || "160";
if (!dropdownCloseFinished) { const UA = navigator.userAgent;
node.animate( const ff = UA.indexOf("Firefox") > -1;
[ if (!dropdownCloseFinished) {
{ node.animate(
top: `${intensity}px`, [
opacity: "0", {
filter: ff ? "none" : `blur(${parseInt(intensity) / 16}px)`, top: `${intensity}px`,
}, opacity: "0",
{ filter: ff ? "none" : `blur(${parseInt(intensity) / 16}px)`,
top: "0", },
opacity: "1", {
filter: ff ? "none" : "blur(0px)", top: "0",
}, opacity: "1",
], filter: ff ? "none" : "blur(0px)",
{ },
easing: transition, ],
duration, {
}, easing: transition,
); duration,
},
return { );
duration: 0,
}; return {
} duration: 0,
// FUCK YOUR DEFAULT SYSTEM, SVELTEKIT!!! };
node.animate( }
[ // FUCK YOUR DEFAULT SYSTEM, SVELTEKIT!!!
{ node.animate(
top: `${-intensity}px`, [
opacity: "0", {
filter: ff ? "none" : `blur(${parseInt(intensity) / 16}px)`, top: `${-intensity}px`,
}, opacity: "0",
{ filter: ff ? "none" : `blur(${parseInt(intensity) / 16}px)`,
top: "0", },
opacity: "1", {
filter: ff ? "none" : "blur(0px)", top: "0",
}, opacity: "1",
], filter: ff ? "none" : "blur(0px)",
{ },
easing: transition, ],
duration, {
}, easing: transition,
); duration,
return { },
duration, );
}; return {
} duration,
};
function transitionOut(node: HTMLElement, { duration = 360 }: TransitionConfig) { }
if ($reducedMotion)
return { function transitionOut(node: HTMLElement, { duration = 360 }: TransitionConfig) {
duration: 0, if ($reducedMotion)
}; return {
node = node.querySelector(".content") || node; duration: 0,
const intensity = node.dataset.intensityOut || "240"; };
if (!dropdownCloseFinished) node = node.querySelector(".content") || node;
return { const intensity = node.dataset.intensityOut || "240";
duration: 0, if (!dropdownCloseFinished)
}; return {
const UA = navigator.userAgent; duration: 0,
const ff = UA.indexOf("Firefox") > -1; };
node.animate( const UA = navigator.userAgent;
[ const ff = UA.indexOf("Firefox") > -1;
{ node.animate(
top: "0", [
opacity: "1", {
filter: ff ? "none" : "blur(0px)", top: "0",
}, opacity: "1",
{ filter: ff ? "none" : "blur(0px)",
top: `${intensity}px`, },
opacity: "0", {
filter: ff ? "none" : `blur(${parseInt(intensity) / 16}px)`, top: `${intensity}px`,
}, opacity: "0",
], filter: ff ? "none" : `blur(${parseInt(intensity) / 16}px)`,
{ },
easing: transition, ],
duration: duration, {
}, easing: transition,
); duration: duration,
return { },
duration, );
}; return {
} duration,
};
let dropdownOpen = false; }
let dropdownCloseFinished = true;
let dropdownOpenFinished = false; let dropdownOpen = false;
// let dropdownOpen = true; let dropdownCloseFinished = true;
// let dropdownCloseFinished = false; let dropdownOpenFinished = false;
// let dropdownOpenFinished = true; // let dropdownOpen = true;
let scrolled = false; // let dropdownCloseFinished = false;
let cookies: { // let dropdownOpenFinished = true;
[key: string]: string; let scrolled = false;
} = {}; let cookies: {
[key: string]: string;
$: navItems = [ } = {};
{
name: "Blog", $: navItems = [
href: "/coming-soon", {
}, name: "Blog",
{ href: "/coming-soon",
name: "Docs", title: "Coming Soon",
href: "/coming-soon", target: "_self",
}, },
{ {
name: "FAQ", name: "Docs",
href: "/faq", href: "/coming-soon",
}, title: "Coming Soon",
{ target: "_self",
name: "Discord", },
href: "https://discord.gg/suyu", {
}, name: "FAQ",
{ href: "/faq",
name: "Git", title: "Coming Soon",
href: "https://git.suyu.dev/suyu/suyu", target: "_self",
}, },
// { {
// name: $token || data.tokenCookie ? "Account" : "Sign up", name: "Compatibility",
// href: $token || data.tokenCookie ? "/account" : "/signup", href: "/compatibility",
// }, title: "Compatibility",
$token || data.tokenCookie target: "_self",
? { },
name: "Account", // {
href: "/account", // name: "Matrix",
} // href: "",
: { // title: "Suyu Matrix Server",
name: "Sign up", // target: "_blank",
href: "/signup", // },
}, {
$token || data.tokenCookie name: "Git",
? { href: "https://git.suyu.dev/suyu/suyu",
name: "Log out", title: "Suyu Git Repo",
href: "/logout", target: "_blank",
} },
: { // {
name: "Log in", // name: $token || data.tokenCookie ? "Account" : "Sign up",
href: "/login", // href: $token || data.tokenCookie ? "/account" : "/signup",
}, // },
] as NavItem[]; $token || data.tokenCookie
? {
$: { name: "Account",
if (browser) { href: "/account",
const html = document.querySelector("html")!; }
if (!dropdownOpen) { : {
dropdownCloseFinished = false; name: "Sign up",
setTimeout(() => { href: "/signup",
html.style.overflowY = "auto"; },
dropdownCloseFinished = true; $token || data.tokenCookie
}, 360); ? {
} else { name: "Log out",
html.style.overflowY = "hidden"; href: "/logout",
dropdownOpenFinished = false; }
setTimeout(() => { : {
dropdownOpenFinished = true; name: "Log in",
}, 360); href: "/login",
} },
} ] as NavItem[];
}
$: {
function getTransition(i: number) { if (browser) {
return `${((i + 1) / 4) * 75}ms`; const html = document.querySelector("html")!;
} if (!dropdownOpen) {
dropdownCloseFinished = false;
if (browser) { setTimeout(() => {
cookies = Object.fromEntries( html.style.overflowY = "auto";
document.cookie.split("; ").map((c) => { dropdownCloseFinished = true;
const [key, value] = c.split("="); }, 360);
return [key, value]; } else {
}), html.style.overflowY = "hidden";
); dropdownOpenFinished = false;
if (cookies.token) { setTimeout(() => {
$token = cookies.token; dropdownOpenFinished = true;
} }, 360);
} }
}
function toggleDropdown() { }
dropdownOpen = !dropdownOpen;
} function getTransition(i: number) {
return `${((i + 1) / 4) * 75}ms`;
setContext("token", token); }
onMount(() => {
const handleScroll = () => { if (browser) {
scrolled = window.scrollY > 0; cookies = Object.fromEntries(
}; document.cookie.split("; ").map((c) => {
const [key, value] = c.split("=");
handleScroll(); // we can't guarantee that the page starts at the top return [key, value];
}),
cookies = Object.fromEntries( );
document.cookie.split("; ").map((c) => { if (cookies.token) {
const [key, value] = c.split("="); $token = cookies.token;
return [key, value]; }
}), }
);
function toggleDropdown() {
window.addEventListener("scroll", handleScroll); dropdownOpen = !dropdownOpen;
}
return () => {
window.removeEventListener("scroll", handleScroll); setContext("token", token);
}; onMount(() => {
}); const handleScroll = () => {
</script> scrolled = window.scrollY > 0;
};
<!-- unfortunately, firefox is horrendous at rendering transforms so we can't enable it there -->
{#if navigator.userAgent.indexOf("Firefox") === -1} handleScroll(); // we can't guarantee that the page starts at the top
<div
class="opacity-5" cookies = Object.fromEntries(
style="position: fixed; width: 100vw; height: 100vh; --mask-image: linear-gradient(to bottom, transparent 50px, black 150px, transparent); mask-image: var(--mask-image); -webkit-mask-image: var(--mask-image);" document.cookie.split("; ").map((c) => {
> const [key, value] = c.split("=");
<BackgroundProvider size={90} gap={16} speed={0.25} /> return [key, value];
</div> }),
{/if} );
<div class="bg"> window.addEventListener("scroll", handleScroll);
<div
style="background: radial-gradient(50% 50%, rgba(255,0,0,0.05), transparent); z-index: -1; width: 800px ;height: 800px; position: fixed; top: -50%; left: calc(25% - 400px);" return () => {
/> window.removeEventListener("scroll", handleScroll);
};
<div });
style="background: radial-gradient(50% 50%, rgba(0,128,255,0.05), transparent); z-index: -1; width: 800px ;height: 800px; position: fixed; top: -50%; right: calc(25% - 400px);" </script>
/>
</div> <!-- unfortunately, firefox is horrendous at rendering transforms so we can't enable it there -->
{#if navigator.userAgent.indexOf("Firefox") === -1}
<main <div
class={`min-h-full w-full ${dropdownOpen || !dropdownCloseFinished ? "overflow-hidden" : ""}`} class="opacity-5"
> style="position: fixed; width: 100vw; height: 100vh; --mask-image: linear-gradient(to bottom, transparent 50px, black 150px, transparent); mask-image: var(--mask-image); -webkit-mask-image: var(--mask-image);"
<header >
style="transition: 180ms ease border;" <BackgroundProvider size={90} gap={16} speed={0.25} />
class={`${ </div>
scrolled {/if}
? "fixed top-0 z-[9999] w-full border-b-2 border-b-[#ffffff11] bg-[#131215d0]"
: "fixed top-0 z-[9999] w-full border-b-0 border-b-[transparent]" <div class="bg">
} pl-[calc(100vw-100%)]`} <div
> style="background: radial-gradient(50% 50%, rgba(255,0,0,0.05), transparent); z-index: -1; width: 800px ;height: 800px; position: fixed; top: -50%; left: calc(25% - 400px);"
<nav />
style="transition: 180ms ease;"
class={scrolled <div
? "mx-auto flex h-[72px] w-full max-w-[1300px] flex-row items-center justify-between px-8 backdrop-blur-xl" style="background: radial-gradient(50% 50%, rgba(0,128,255,0.05), transparent); z-index: -1; width: 800px ;height: 800px; position: fixed; top: -50%; right: calc(25% - 400px);"
: "mx-auto flex h-[120px] w-full max-w-[1300px] flex-row items-center justify-between px-8"} />
> </div>
<div class="flex w-full flex-row items-center justify-start gap-8">
<a <main
href="/" class={`min-h-full w-full ${dropdownOpen || !dropdownCloseFinished ? "overflow-hidden" : ""}`}
on:click={() => { >
if (dropdownOpen && window.innerWidth < 625) toggleDropdown(); <header
}} style="transition: 180ms ease border;"
> class={`${
<Logo size={28} /> scrolled
</a> ? "fixed top-0 z-[9999] w-full border-b-2 border-b-[#ffffff11] bg-[#131215d0]"
</div> : "fixed top-0 z-[9999] w-full border-b-0 border-b-[transparent]"
<div } pl-[calc(100vw-100%)]`}
class="flex w-full flex-row items-center justify-center gap-2 text-sm font-medium text-[#A6A5A7] max-[625px]:hidden" >
> <nav
<a href="/coming-soon" class="px-5 py-3 transition hover:text-white">Blog</a> style="transition: 180ms ease;"
<a href="/coming-soon" class="px-5 py-3 transition hover:text-white">Docs</a> class={scrolled
<a href="/faq" class="px-5 py-3 transition hover:text-white">FAQ</a> ? "mx-auto flex h-[72px] w-full max-w-[1300px] flex-row items-center justify-between px-8 backdrop-blur-xl"
</div> : "mx-auto flex h-[120px] w-full max-w-[1300px] flex-row items-center justify-between px-8"}
<div class="flex w-full flex-row items-center justify-end text-[#A6A5A7]"> >
<div class="flex flex-row gap-4 max-[625px]:hidden"> <div class="flex w-full flex-row items-center justify-start gap-8">
<a <a
class="p-2 transition hover:text-white" href="/"
href="https://git.suyu.dev/suyu/suyu" title="Suyu Home"
rel="noreferrer noopener" on:click={() => {
target="_blank" if (dropdownOpen && window.innerWidth < 800) toggleDropdown();
> }}
<CodeBranchOutline /> >
</a> <Logo size={28} />
<a </a>
class="p-2 transition hover:text-white" </div>
href="https://discord.gg/suyu" <div
rel="noreferrer noopener" class="flex w-full flex-row items-center justify-center gap-2 text-sm font-medium text-[#A6A5A7] max-[800px]:hidden"
target="_blank" >
> <a href="/coming-soon" class="px-5 py-3 transition hover:text-white" title="Coming Soon">Blog</a>
<DiscordSolid /> <a href="/coming-soon" class="px-5 py-3 transition hover:text-white" title="Coming Soon">Docs</a>
</a> <a href="/faq" class="px-5 py-3 transition hover:text-white" title="Coming Soon">FAQ</a>
{#if $token} <a href="/compatibility" class="px-5 py-3 transition hover:text-white" title="Compatibility">Compatibility</a>
<!-- <a href={$token ? "/account" : "/signup"} class="button-sm" </div>
>{$token ? "Account" : "Sign up"}</a <div class="flex w-full flex-row items-center justify-end text-[#A6A5A7]">
> --> <div class="flex flex-row gap-4 max-[800px]:hidden">
<!-- <a href="/account" class="button-sm">Account</a> --> <a
<AccountButton user={data.user} /> class="p-2 transition hover:text-white"
{:else} href="https://git.suyu.dev/suyu/suyu"
<a href="/login" class="button-sm">Log in</a> rel="noreferrer noopener"
<a href="/signup" class="button-sm">Sign up</a> target="_blank"
{/if} title="Suyu Git Repo"
</div> >
<div class="relative mr-4 hidden flex-row gap-4 max-[625px]:flex"> <CodeBranchOutline />
<button </a>
aria-label={dropdownOpen ? "Close navigation" : "Open navigation"} <!-- <a
aria-expanded={dropdownOpen} class="p-2 transition hover:text-white"
on:click={toggleDropdown} href=""
class="-mr-4 p-4" rel="noreferrer noopener"
> target="_blank"
<div title="Suyu Matrix Server"
style="transition: 180ms; transition-property: opacity transform;" >
class={`absolute ${dropdownOpen ? "rotate-45 opacity-0" : "opacity-1"}`} <DiscordSolid />
> </a> -->
<BarsSolid /> {#if $token}
</div> <!-- <a href={$token ? "/account" : "/signup"} class="button-sm"
<div >{$token ? "Account" : "Sign up"}</a
style="transition: 180ms; transition-property: opacity transform;" > -->
class={dropdownOpen <!-- <a href="/account" class="button-sm">Account</a> -->
? "opacity-1 rotate-0" <AccountButton user={data.user} />
: "rotate-[-45deg] opacity-0"} {:else}
> <a href="/login" class="button-sm">Log in</a>
<CloseSolid /> <a href="/signup" class="button-sm">Sign up</a>
</div> {/if}
</button> </div>
</div> <div class="relative mr-4 hidden flex-row gap-4 max-[800px]:flex">
</div> <button
</nav> aria-label={dropdownOpen ? "Close navigation" : "Open navigation"}
</header> aria-expanded={dropdownOpen}
<div on:click={toggleDropdown}
style="transition: 180ms ease;" class="-mr-4 p-4"
aria-hidden={!dropdownOpenFinished && !dropdownOpen} >
class={`fixed left-0 z-[100] h-screen w-full bg-[#0e0d10] p-9 pt-[120px] ${dropdownOpen ? "pointer-events-auto visible opacity-100" : "pointer-events-none opacity-0"} ${!dropdownOpen && dropdownCloseFinished ? "invisible" : ""}`} <div
> style="transition: 180ms; transition-property: opacity transform;"
<div class={`flex flex-col gap-8`}> class={`absolute ${dropdownOpen ? "rotate-45 opacity-0" : "opacity-1"}`}
<!-- <a href="##"><h1 class="w-full text-5xl">Blog</h1></a> >
<a href="##"><h1 class="w-full text-5xl">Docs</h1></a> <BarsSolid />
<a href="##"><h1 class="w-full text-5xl">FAQ</h1></a> --> </div>
{#each navItems as item, i} <div
<a style="transition: 180ms; transition-property: opacity transform;"
style={`transition: ${ class={dropdownOpen
dropdownOpen ? "opacity-1 rotate-0"
? generateTransition([ : "rotate-[-45deg] opacity-0"}
// { >
// duration: 600, <CloseSolid />
// delay: (i + 1) / 4, </div>
// property: "transform", </button>
// timingFunction: transition, </div>
// }, </div>
// { </nav>
// duration: 500, </header>
// delay: i * 1.25, <div
// property: "filter", style="transition: 180ms ease;"
// timingFunction: transition, aria-hidden={!dropdownOpenFinished && !dropdownOpen}
// }, class={`fixed left-0 z-[100] h-screen w-full bg-[#0e0d10] p-9 pt-[120px] ${dropdownOpen ? "pointer-events-auto visible opacity-100" : "pointer-events-none opacity-0"} ${!dropdownOpen && dropdownCloseFinished ? "invisible" : ""}`}
// { >
// duration: 400, <div class={`flex flex-col gap-8`}>
// delay: (i + 1) / 4, <!-- <a href="##"><h1 class="w-full text-5xl">Blog</h1></a>
// property: "opacity", <a href="##"><h1 class="w-full text-5xl">Docs</h1></a>
// timingFunction: transition, <a href="##"><h1 class="w-full text-5xl">FAQ</h1></a> -->
// }, {#each navItems as item, i}
{ <a
duration: 450, style={`transition: ${
delay: i * 0.6, dropdownOpen
property: ["transform", "opacity", "filter"], ? generateTransition([
timingFunction: transition, // {
}, // duration: 600,
]) // delay: (i + 1) / 4,
: generateTransition([ // property: "transform",
{ // timingFunction: transition,
duration: 450, // },
delay: 0, // {
property: ["transform", "opacity", "filter"], // duration: 500,
timingFunction: transition, // delay: i * 1.25,
}, // property: "filter",
]) // timingFunction: transition,
}`} // },
class="{dropdownOpen // {
? 'translate-y-0 opacity-100 filter-none' // duration: 400,
: '-translate-y-24 opacity-0 blur-md'} " // delay: (i + 1) / 4,
href={item.href} // property: "opacity",
on:click={() => toggleDropdown()} // timingFunction: transition,
> // },
<h1 class="w-full text-5xl">{item.name}</h1> {
</a> duration: 450,
{/each} delay: i * 0.6,
</div> property: ["transform", "opacity", "filter"],
</div> timingFunction: transition,
<!-- svelte-ignore a11y-no-noninteractive-tabindex --> },
{#key data.url} ])
<div : generateTransition([
in:transitionIn={{ duration: 500 }} {
out:transitionOut={{ duration: 500 }} duration: 450,
aria-hidden={dropdownOpenFinished && dropdownOpen} delay: 0,
tabindex={dropdownOpen ? 0 : -1} property: ["transform", "opacity", "filter"],
class={`absolute left-[50%] z-50 mx-auto flex w-screen max-w-[1300px] translate-x-[-50%] flex-col px-8 pb-12 pt-[120px] ${dropdownOpen ? "pointer-events-none translate-y-[25vh] opacity-0" : ""} ${dropdownOpenFinished && dropdownOpen ? "invisible" : ""}`} timingFunction: transition,
> },
<slot /> ])
</div> }`}
{/key} class="{dropdownOpen
</main> ? 'translate-y-0 opacity-100 filter-none'
: '-translate-y-24 opacity-0 blur-md'} "
href={item.href}
title={item.title}
target={item.target}
on:click={() => toggleDropdown()}
>
<h1 class="w-full text-5xl">{item.name}</h1>
</a>
{/each}
</div>
</div>
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
{#key data.url}
<div
in:transitionIn={{ duration: 500 }}
out:transitionOut={{ duration: 500 }}
aria-hidden={dropdownOpenFinished && dropdownOpen}
tabindex={dropdownOpen ? 0 : -1}
class={`absolute left-[50%] z-50 mx-auto flex w-screen max-w-[1300px] translate-x-[-50%] flex-col px-8 pb-12 pt-[120px] ${dropdownOpen ? "pointer-events-none translate-y-[25vh] opacity-0" : ""} ${dropdownOpenFinished && dropdownOpen ? "invisible" : ""}`}
>
<slot />
</div>
{/key}
</main>

View file

@ -1,271 +1,281 @@
<script lang="ts"> <script lang="ts">
import embedImage from "$assets/branding/suyu__Embed-Image.png"; import embedImage from "$assets/branding/suyu__Embed-Image.png";
import type { PageData } from "./$types"; import type { PageData } from "./$types";
import suyuWindow from "$assets/mockups/suyuwindow.png"; import suyuWindow from "$assets/mockups/suyuwindow.png";
import HomepageCounter from "$components/HomepageCounter.svelte"; import HomepageCounter from "$components/HomepageCounter.svelte";
import { XCircleOutline } from "flowbite-svelte-icons"; import { XCircleOutline } from "flowbite-svelte-icons";
import { Dialog } from "radix-svelte"; import { Dialog } from "radix-svelte";
export let data: PageData; export let data: PageData;
$: memberCount = parseFloat(data.memberCount?.toPrecision(2)); $: memberCount = parseFloat(data.memberCount?.toPrecision(2));
$: contributors = parseFloat(data.roleMembers?.["1214817156420862012"]?.toPrecision(2)); $: contributors = parseFloat(data.roleMembers["1214817156420862012"]?.toPrecision(2));
$: gitCommits = parseFloat(data.gitCommits?.toPrecision(2)); $: starCount = parseFloat(data.starCount?.toPrecision(2));
let metadata = { let metadata = {
url: "https://suyu.dev", url: "https://suyu.dev",
title: "suyu - Open-source, non-profit Switch emulator", title: "suyu - Open-source, non-profit Switch emulator",
description: description:
"suyu is a familiar C++ based Nintendo Switch emulator with a focus on compatibility. Completely free and open-source, forever. Download it here.", "suyu is a familiar C++ based Nintendo Switch emulator with a focus on compatibility. Completely free and open-source, forever. Download it here.",
image: embedImage, image: embedImage,
}; };
let rootOpen: boolean; let rootOpen: boolean;
let rootModal: boolean = true; let rootModal: boolean = true;
let contentOpenAutoFocus: boolean = true; let contentOpenAutoFocus: boolean = true;
let contentCloseAutoFocus: boolean = true; let contentCloseAutoFocus: boolean = true;
</script> </script>
<svelte:head> <svelte:head>
<title>{metadata.title}</title> <title>{metadata.title}</title>
<meta name="description" content={metadata.description} /> <meta name="description" content={metadata.description} />
<meta property="og:url" content={metadata.url} /> <meta property="og:url" content={metadata.url} />
<meta property="og:title" content={metadata.title} /> <meta property="og:title" content={metadata.title} />
<meta property="og:description" content={metadata.description} /> <meta property="og:description" content={metadata.description} />
<meta property="og:image" content={metadata.image} /> <meta property="og:image" content={metadata.image} />
<meta name="twitter:url" content={metadata.url} /> <meta name="twitter:url" content={metadata.url} />
<meta name="twitter:title" content={metadata.title} /> <meta name="twitter:title" content={metadata.title} />
<meta name="twitter:description" content={metadata.description} /> <meta name="twitter:description" content={metadata.description} />
<meta name="twitter:image" content={metadata.image} /> <meta name="twitter:image" content={metadata.image} />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
</svelte:head> </svelte:head>
<div <article class="border-l-[4px] border-solid border-color-[#DBDBDB] rounded-[10px] bg-[#110d10] pt-[10px] inline-block mb-[20px]">
class="relative flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] rounded-bl-none rounded-br-none bg-[#110d10] p-8 md:p-12 lg:rounded-bl-none lg:rounded-br-[2.25rem]" <div class="ml-[10px]">
> <p class="text-[20px] font-bold mb-[2px]">Notice: Suyu Discord Deleted</p>
<svg <p class="text-[#A6A5A7] pb-[10px] mr-[12px]">The Suyu Discord Server has been unlawfully DMCA'd, by who we believe is Nintendo Co., Ltd. Whilst we are not pressing charges, we will continue with the development of the emulator as intended. We are moving our communications to a self-hosted Matrix Server for the foreseeable future. Please check back within the next 48 hours for an update as our dev team works hard behind the scenes to get the server implemented. Thank you for sticking with us through this tough journey. We will continue to fight for the right to legal emulation, as shown in <a href="https://www.copyright.gov/fair-use/summaries/sony-connectix-9thcir2000.pdf" target="_blank" class="transition hover:text-[#f94d4d] text-[#60c7e9]"><i>Sega Enterprises Ltd. v. Accolade, Inc.</i></a> and <a href="https://www.copyright.gov/fair-use/summaries/sony-connectix-9thcir2000.pdf" target="_blank" class="transition hover:text-[#f94d4d] text-[#60c7e9]"><i>Sony Computer Entertainment v. Connectix Corporation</i></a>. We are curerntly in the process of auditing our code for copyright infringing content. If you would like to assist, please contribute to the project via our Git. Thank you.</p>
xmlns="http://www.w3.org/2000/svg" </div>
width="512" </article>
height="525" <div
viewBox="0 0 512 525" class="relative flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] rounded-bl-none rounded-br-none bg-[#110d10] p-8 md:p-12 lg:rounded-bl-none lg:rounded-br-[2.25rem]"
fill="none" >
style="animation-duration: 300s; transform-origin: 50% 50%; animation-iteration-count: infinite; animation-timing-function: linear; animation-name: spin; animation-delay: 0s; animation-direction: normal; animation-fill-mode: none; animation-play-state: running;" <svg
class="pointer-events-none absolute -bottom-[18rem] right-0 z-0 animate-spin opacity-20" xmlns="http://www.w3.org/2000/svg"
> width="512"
<path height="525"
d="M511.5 262.12C511.5 353.613 465.547 434.182 396.019 480.947C408.179 457.937 415.083 431.597 415.083 403.617C415.083 313.723 343.816 240.744 255.992 240.744C191.257 240.744 138.692 186.941 138.692 120.622C138.692 54.3027 191.257 0.5 255.992 0.5C397.026 0.5 511.5 117.695 511.5 262.12ZM255.992 53.5225C243.745 53.5225 233.816 63.7047 233.816 76.2224C233.816 88.7388 243.745 98.9223 255.992 98.9223C268.257 98.9223 278.173 88.7387 278.173 76.2224C278.173 63.7048 268.257 53.5225 255.992 53.5225ZM299.355 97.9223C287.104 97.9223 277.173 108.104 277.173 120.622C277.173 133.139 287.104 143.322 299.355 143.322C311.62 143.322 321.536 133.139 321.536 120.622C321.536 108.104 311.62 97.9223 299.355 97.9223ZM212.635 97.9223C200.382 97.9223 190.455 108.104 190.455 120.622C190.455 133.139 200.382 143.322 212.635 143.322C224.889 143.322 234.816 133.139 234.816 120.622C234.816 108.104 224.888 97.9223 212.635 97.9223ZM255.992 142.322C243.745 142.322 233.816 152.505 233.816 165.021C233.816 177.539 243.745 187.721 255.992 187.721C268.257 187.721 278.173 177.538 278.173 165.021C278.173 152.505 268.257 142.322 255.992 142.322Z" viewBox="0 0 512 525"
stroke="white" fill="none"
/> style="animation-duration: 300s; transform-origin: 50% 50%; animation-iteration-count: infinite; animation-timing-function: linear; animation-name: spin; animation-delay: 0s; animation-direction: normal; animation-fill-mode: none; animation-play-state: running;"
<path class="pointer-events-none absolute -bottom-[18rem] right-0 z-0 animate-spin opacity-20"
d="M0.5 262.119C0.5 170.626 46.444 90.0553 115.976 43.2909C103.82 66.3019 96.9172 92.6424 96.9172 120.622C96.9172 210.516 168.174 283.495 255.992 283.495C320.735 283.495 373.305 337.298 373.305 403.617C373.305 469.934 320.735 523.739 255.992 523.739C114.974 523.739 0.5 406.544 0.5 262.119ZM255.992 336.517C243.744 336.517 233.816 346.7 233.816 359.217C233.816 371.735 243.745 381.917 255.992 381.917C268.256 381.917 278.173 371.735 278.173 359.217C278.173 346.701 268.256 336.517 255.992 336.517ZM299.355 380.917C287.104 380.917 277.173 391.099 277.173 403.617C277.173 416.135 287.104 426.317 299.355 426.317C311.619 426.317 321.536 416.135 321.536 403.617C321.536 391.099 311.619 380.917 299.355 380.917ZM255.992 425.317C243.745 425.317 233.816 435.499 233.816 448.016C233.816 460.533 243.744 470.717 255.992 470.717C268.256 470.717 278.173 460.533 278.173 448.016C278.173 435.499 268.256 425.317 255.992 425.317ZM212.634 380.917C200.382 380.917 190.454 391.099 190.454 403.617C190.454 416.135 200.382 426.317 212.634 426.317C224.888 426.317 234.816 416.135 234.816 403.617C234.816 391.099 224.888 380.917 212.634 380.917Z" >
stroke="white" <path
/> d="M511.5 262.12C511.5 353.613 465.547 434.182 396.019 480.947C408.179 457.937 415.083 431.597 415.083 403.617C415.083 313.723 343.816 240.744 255.992 240.744C191.257 240.744 138.692 186.941 138.692 120.622C138.692 54.3027 191.257 0.5 255.992 0.5C397.026 0.5 511.5 117.695 511.5 262.12ZM255.992 53.5225C243.745 53.5225 233.816 63.7047 233.816 76.2224C233.816 88.7388 243.745 98.9223 255.992 98.9223C268.257 98.9223 278.173 88.7387 278.173 76.2224C278.173 63.7048 268.257 53.5225 255.992 53.5225ZM299.355 97.9223C287.104 97.9223 277.173 108.104 277.173 120.622C277.173 133.139 287.104 143.322 299.355 143.322C311.62 143.322 321.536 133.139 321.536 120.622C321.536 108.104 311.62 97.9223 299.355 97.9223ZM212.635 97.9223C200.382 97.9223 190.455 108.104 190.455 120.622C190.455 133.139 200.382 143.322 212.635 143.322C224.889 143.322 234.816 133.139 234.816 120.622C234.816 108.104 224.888 97.9223 212.635 97.9223ZM255.992 142.322C243.745 142.322 233.816 152.505 233.816 165.021C233.816 177.539 243.745 187.721 255.992 187.721C268.257 187.721 278.173 177.538 278.173 165.021C278.173 152.505 268.257 142.322 255.992 142.322Z"
</svg> stroke="white"
<h1 class="text-[24px] leading-[1.41] md:text-[56px] md:leading-[1.1]"> />
<span class="font-bold text-[#60c7e9]">suyu</span> is a fully open-source <path
<span class="font-bold text-[#f94d4d]">Switch</span> emulator d="M0.5 262.119C0.5 170.626 46.444 90.0553 115.976 43.2909C103.82 66.3019 96.9172 92.6424 96.9172 120.622C96.9172 210.516 168.174 283.495 255.992 283.495C320.735 283.495 373.305 337.298 373.305 403.617C373.305 469.934 320.735 523.739 255.992 523.739C114.974 523.739 0.5 406.544 0.5 262.119ZM255.992 336.517C243.744 336.517 233.816 346.7 233.816 359.217C233.816 371.735 243.745 381.917 255.992 381.917C268.256 381.917 278.173 371.735 278.173 359.217C278.173 346.701 268.256 336.517 255.992 336.517ZM299.355 380.917C287.104 380.917 277.173 391.099 277.173 403.617C277.173 416.135 287.104 426.317 299.355 426.317C311.619 426.317 321.536 416.135 321.536 403.617C321.536 391.099 311.619 380.917 299.355 380.917ZM255.992 425.317C243.745 425.317 233.816 435.499 233.816 448.016C233.816 460.533 243.744 470.717 255.992 470.717C268.256 470.717 278.173 460.533 278.173 448.016C278.173 435.499 268.256 425.317 255.992 425.317ZM212.634 380.917C200.382 380.917 190.454 391.099 190.454 403.617C190.454 416.135 200.382 426.317 212.634 426.317C224.888 426.317 234.816 416.135 234.816 403.617C234.816 391.099 224.888 380.917 212.634 380.917Z"
</h1> stroke="white"
<p class="max-w-[36rem] text-lg leading-relaxed text-[#A6A5A7]"> />
suyu is a familiar C++ based Switch emulator with a focus on compatibility. Completely free </svg>
and open-source, forever. <h1 class="text-[24px] leading-[1.41] md:text-[56px] md:leading-[1.1]">
</p> <span class="font-bold text-[#60c7e9]">suyu</span> is a fully open-source
<div class="flex flex-col gap-4 md:flex-row"> <span class="font-bold text-[#f94d4d]">Switch</span> emulator
<a href="/download" rel="noreferrer noopener" class="cta-button"> </h1>
Download <svg <p class="max-w-[36rem] text-lg leading-relaxed text-[#A6A5A7]">
width="16" suyu is a familiar C++ based Switch emulator with a focus on compatibility. Completely free
height="16" and open-source, forever.
viewBox="0 0 16 16" </p>
fill="currentColor" <div class="flex flex-col gap-4 md:flex-row">
role="img" <a
focusable="false" href="/download"
aria-hidden="true" rel="noreferrer noopener"
><path class="cta-button"
d="M5.46967 11.4697C5.17678 11.7626 5.17678 12.2374 5.46967 12.5303C5.76256 12.8232 6.23744 12.8232 6.53033 12.5303L10.5303 8.53033C10.8207 8.23999 10.8236 7.77014 10.5368 7.47624L6.63419 3.47624C6.34492 3.17976 5.87009 3.17391 5.57361 3.46318C5.27713 3.75244 5.27128 4.22728 5.56054 4.52376L8.94583 7.99351L5.46967 11.4697Z" title="Download Suyu"
></path></svg >
> Download <svg
</a> width="16"
<a height="16"
href="https://git.suyu.dev/suyu/suyu" viewBox="0 0 16 16"
target="_blank" fill="currentColor"
rel="noreferrer noopener" role="img"
class="button text-[#8A8F98]" focusable="false"
> aria-hidden="true"
Contribute <svg ><path
width="16" d="M5.46967 11.4697C5.17678 11.7626 5.17678 12.2374 5.46967 12.5303C5.76256 12.8232 6.23744 12.8232 6.53033 12.5303L10.5303 8.53033C10.8207 8.23999 10.8236 7.77014 10.5368 7.47624L6.63419 3.47624C6.34492 3.17976 5.87009 3.17391 5.57361 3.46318C5.27713 3.75244 5.27128 4.22728 5.56054 4.52376L8.94583 7.99351L5.46967 11.4697Z"
height="16" ></path></svg
viewBox="0 0 16 16" >
fill="currentColor" </a>
role="img" <a
focusable="false" href="https://git.suyu.dev/suyu/suyu"
aria-hidden="true" target="_blank"
><path rel="noreferrer noopener"
d="M5.46967 11.4697C5.17678 11.7626 5.17678 12.2374 5.46967 12.5303C5.76256 12.8232 6.23744 12.8232 6.53033 12.5303L10.5303 8.53033C10.8207 8.23999 10.8236 7.77014 10.5368 7.47624L6.63419 3.47624C6.34492 3.17976 5.87009 3.17391 5.57361 3.46318C5.27713 3.75244 5.27128 4.22728 5.56054 4.52376L8.94583 7.99351L5.46967 11.4697Z" class="button text-[#8A8F98]"
></path></svg title="Suyu Git Repo"
> >
</a> Contribute <svg
</div> width="16"
</div> height="16"
viewBox="0 0 16 16"
<div class="flex w-full flex-col lg:flex-row"> fill="currentColor"
<div role="img"
class="flex w-full flex-shrink-0 flex-col gap-8 rounded-b-[2.25rem] bg-[#110d10] p-12 lg:w-[35%]" focusable="false"
> aria-hidden="true"
<h1 class="text-[48px] leading-[0.9]">By the numbers</h1> ><path
<HomepageCounter count={contributors} subText="dedicated contributors" /> d="M5.46967 11.4697C5.17678 11.7626 5.17678 12.2374 5.46967 12.5303C5.76256 12.8232 6.23744 12.8232 6.53033 12.5303L10.5303 8.53033C10.8207 8.23999 10.8236 7.77014 10.5368 7.47624L6.63419 3.47624C6.34492 3.17976 5.87009 3.17391 5.57361 3.46318C5.27713 3.75244 5.27128 4.22728 5.56054 4.52376L8.94583 7.99351L5.46967 11.4697Z"
<HomepageCounter count={gitCommits} subText="Git commits" /> ></path></svg
<HomepageCounter count={4000} subText="supported games" /> >
<HomepageCounter count={memberCount} subText="members on Discord" /> </a>
</div> </div>
<div class="flex w-full flex-1 rounded-[2.25rem] bg-[#110d10] lg:rounded-tl-none"> </div>
<div
class="flex w-full flex-1 rounded-[2.25rem] rounded-bl-none rounded-tr-none bg-[#131215] pt-8 lg:pl-8" <div class="flex w-full flex-col lg:flex-row">
> <div
<div class="flex w-full flex-shrink-0 flex-col gap-8 rounded-b-[2.25rem] bg-[#110d10] p-12 lg:w-[35%]"
class="h-full w-full overflow-hidden rounded-[2.25rem] bg-[#110d10] shadow-lg ring ring-[#ffffff11]" >
> <h1 class="text-[48px] leading-[0.9]">By the numbers</h1>
<img <HomepageCounter count={contributors} subText="dedicated contributors" />
class="h-full max-h-[496px] w-full object-cover" <HomepageCounter count={starCount} subText="GitLab stars" />
alt="" <HomepageCounter count={4000} subText="supported games" />
decoding="async" <HomepageCounter count={memberCount} subText="members on Discord" />
loading="lazy" </div>
src={suyuWindow} <div class="flex w-full flex-1 rounded-[2.25rem] bg-[#110d10] lg:rounded-tl-none">
/> <div
</div> class="flex w-full flex-1 rounded-[2.25rem] rounded-bl-none rounded-tr-none bg-[#131215] pt-8 lg:pl-8"
</div> >
</div> <div
</div> class="h-full w-full overflow-hidden rounded-[2.25rem] bg-[#110d10] shadow-lg ring ring-[#ffffff11]"
>
<div <img
class="relative mt-48 flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] bg-[#eee] p-8 text-black md:p-12" class="h-full max-h-[496px] w-full object-cover"
> alt=""
<h1 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]"> decoding="async"
Built by and for the community loading="lazy"
</h1> src={suyuWindow}
<p class="max-w-[40rem] text-lg leading-relaxed text-[#4D4D4D]"> />
The future of suyu is shaped by you. New features are always being added, and our community </div>
continually shapes the direction of the project. </div>
</p> </div>
</div> </div>
<div class="mt-8 flex w-full flex-col gap-8 lg:flex-row"> <div
<a class="relative mt-48 flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] bg-[#eee] p-8 text-black md:p-12"
href="https://discord.gg/suyu/" >
target="_blank" <h1 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]">
rel="noreferrer noopener" Built by and for the community
class="relative w-full rounded-[2.25rem] bg-[#5451ff] p-12" </h1>
> <p class="max-w-[40rem] text-lg leading-relaxed text-[#4D4D4D]">
<h2 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]">Discord</h2> The future of suyu is shaped by you. New features are always being added, and our community
<p class="mt-2 text-lg leading-relaxed"> continually shapes the direction of the project.
Join our Discord server to chat with {memberCount}+ suyu users and developers. Get the </p>
latest updates and help with any issues you have. </div>
</p>
<svg <div class="mt-8 flex w-full flex-col gap-8 lg:flex-row">
xmlns="http://www.w3.org/2000/svg" <div
fill="none" class="relative w-full rounded-[2.25rem] bg-[#662d91] p-12"
viewBox="0 0 24 24" title="Suyu Matrix Server"
stroke="currentColor" >
class="absolute right-12 top-12 h-12 w-12" <h2 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]">Matrix</h2>
> <p class="mt-2 text-lg leading-relaxed">
<path Our new Matrix Server is currently being implemented. Please check back here soon for more details. Please view the notice at the top of the page for more information. Thank you.
stroke-linecap="round" </p>
stroke-linejoin="round" <svg
stroke-width="4" xmlns="http://www.w3.org/2000/svg"
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" fill="none"
/> viewBox="0 0 24 24"
</svg> stroke="currentColor"
</a> class="absolute right-12 top-12 h-12 w-12"
<a >
href="https://git.suyu.dev/suyu/suyu" <!-- <path
target="_blank" stroke-linecap="round"
rel="noreferrer noopener" stroke-linejoin="round"
class="relative w-full rounded-[2.25rem] bg-[#f78c40] p-12 text-black" stroke-width="4"
> d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
<h2 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]">Git</h2> /> -->
<p class="mt-2 text-lg leading-relaxed"> </svg>
Our Git instance is where all the magic of suyu happens. We're always looking for new </div>
contributors to help us out, so feel free to check out our code. <a
</p> href="https://git.suyu.dev/suyu/suyu"
<svg target="_blank"
xmlns="http://www.w3.org/2000/svg" rel="noreferrer noopener"
fill="none" class="relative w-full rounded-[2.25rem] bg-[#f78c40] p-12 text-black"
viewBox="0 0 24 24" title="Suyu Git Repo"
stroke="currentColor" >
class="absolute right-12 top-12 h-12 w-12" <h2 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]">Git</h2>
> <p class="mt-2 text-lg leading-relaxed">
<path Our Git instance is where all the magic of suyu happens. We're always looking for new contributors
stroke-linecap="round" to help us out, so feel free to check out our code.
stroke-linejoin="round" </p>
stroke-width="4" <svg
d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" xmlns="http://www.w3.org/2000/svg"
/> fill="none"
</svg> viewBox="0 0 24 24"
</a> stroke="currentColor"
</div> class="absolute right-12 top-12 h-12 w-12"
>
<div <path
class="relative mt-48 flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] bg-[#110d10] p-8 md:p-12" stroke-linecap="round"
> stroke-linejoin="round"
<h1 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]"> stroke-width="4"
were passionate about preserving games. d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"
</h1> />
<p class="max-w-[40rem] text-lg leading-relaxed text-[#A6A5A7]"> </svg>
Were developing suyu as a tool to prevent Switch games from becoming lost media, so were </a>
taking care to prevent dissolution from outside influences. </div>
</p>
<div class="flex flex-col gap-4"> <div
<div class="flex flex-row items-center gap-2"> class="relative mt-48 flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] bg-[#110d10] p-8 md:p-12"
<svg >
xmlns="http://www.w3.org/2000/svg" <h1 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]">
fill="none" were passionate about preserving games.
viewBox="0 0 24 24" </h1>
stroke-width="1.5" <p class="max-w-[40rem] text-lg leading-relaxed text-[#A6A5A7]">
stroke="currentColor" Were developing suyu as a tool to prevent Switch games from becoming lost media, so were
class="h-6 w-6" taking care to prevent dissolution from outside influences.
> </p>
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" /> <div class="flex flex-col gap-4">
</svg> <div class="flex flex-row items-center gap-2">
<p>suyu is community-run.</p> <svg
</div> xmlns="http://www.w3.org/2000/svg"
<div class="flex flex-row items-center gap-2"> fill="none"
<svg viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" stroke-width="1.5"
fill="none" stroke="currentColor"
viewBox="0 0 24 24" class="h-6 w-6"
stroke-width="1.5" >
stroke="currentColor" <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
class="h-6 w-6" </svg>
> <p>suyu is community-run.</p>
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" /> </div>
</svg> <div class="flex flex-row items-center gap-2">
<p>suyu is completely free. Theres no option to monetarily support the project.</p> <svg
</div> xmlns="http://www.w3.org/2000/svg"
<div class="flex flex-row items-center gap-2"> fill="none"
<svg viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" stroke-width="1.5"
fill="none" stroke="currentColor"
viewBox="0 0 24 24" class="h-6 w-6"
stroke-width="1.5" >
stroke="currentColor" <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
class="h-6 w-6" </svg>
> <p>suyu is completely free. Theres no option to monetarily support the project.</p>
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" /> </div>
</svg> <div class="flex flex-row items-center gap-2">
<p>suyu exists solely as an effort of hardware preservation and research.</p> <svg
</div> xmlns="http://www.w3.org/2000/svg"
<div class="flex flex-row items-center gap-2"> fill="none"
<svg viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg" stroke-width="1.5"
fill="none" stroke="currentColor"
viewBox="0 0 24 24" class="h-6 w-6"
stroke-width="1.5" >
stroke="currentColor" <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
class="h-6 w-6" </svg>
> <p>suyu exists solely as an effort of hardware preservation and research.</p>
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" /> </div>
</svg> <div class="flex flex-row items-center gap-2">
<p>suyu does not condone nor facilitate piracy or intellectual property theft.</p> <svg
</div> xmlns="http://www.w3.org/2000/svg"
</div> fill="none"
</div> viewBox="0 0 24 24"
stroke-width="1.5"
<div data-spacer-element class="min-h-[180px]"></div> stroke="currentColor"
class="h-6 w-6"
>
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
</svg>
<p>suyu does not condone nor facilitate piracy or intellectual property theft.</p>
</div>
</div>
</div>
<div data-spacer-element class="min-h-[180px]"></div>