fix fonts, colors, etc

This commit is contained in:
not-nullptr 2024-03-16 02:44:43 +00:00
parent 69f7976702
commit 55edefd906
19 changed files with 242 additions and 45 deletions

View file

@ -7,7 +7,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Jost:ital,wght@0,100..900;1,100..900&display=swap"
href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap"
rel="stylesheet"
/>
<meta property="og:type" content="summary" />

View file

@ -4,14 +4,6 @@
@tailwind components;
@tailwind utilities;
@font-face {
font-family: "Roc Grotesk";
src: url(./assets/fonts/RocGroteskWideMedium.ttf) format("truetype");
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Consolas";
src: url(./assets/fonts/Consolas.ttf) format("truetype");
@ -37,7 +29,7 @@ html {
h1,
h2,
h3 {
font-family: "Roc Grotesk", sans-serif;
font-family: "Outfit", sans-serif;
}
::selection {

View file

@ -213,6 +213,14 @@
});
</script>
<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);"
/>
<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);"
/>
<main
class={`min-h-full w-full ${dropdownOpen || !dropdownCloseFinished ? "overflow-hidden" : ""}`}
>

View file

@ -40,7 +40,7 @@
</svelte:head>
<div
class="relative flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] rounded-bl-none rounded-br-none bg-black p-8 md:p-12 lg:rounded-bl-none lg:rounded-br-[2.25rem]"
class="relative flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] rounded-bl-none rounded-br-none bg-[rgba(0,0,0,0.25)] p-8 backdrop-blur-xl md:p-12 lg:rounded-bl-none lg:rounded-br-[2.25rem]"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -60,8 +60,9 @@
stroke="white"
/>
</svg>
<h1 class="text-[24px] leading-[1.41] md:text-[60px] md:leading-[1.1]">
suyu is an open-source, non-profit Switch emulator
<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
<span class="font-bold text-[#f94d4d]">Switch</span> emulator
</h1>
<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

View file

@ -22,7 +22,7 @@
const navItems: NavItem[] = [
{
name: "Online Services",
name: "Multiplayer",
href: "/account",
},
{
@ -30,8 +30,8 @@
href: "/account/lobbies",
},
{
name: "Settings",
href: "/account/settings",
name: "Friends",
href: "/account/friends",
},
];
@ -54,7 +54,11 @@
const pillBounds = indicator.getBoundingClientRect();
indicator.style.transform = `translateX(${bounds.left - navBounds.left}px)`;
indicator.style.width = `${bounds.width}px`;
if ((selected !== 0 && selected !== navItems.length - 1) || $reducedMotion) return;
if (
// (selected !== 0 && selected !== navItems.length - 1) ||
$reducedMotion
)
return;
indicator.offsetHeight;
const transformFactor = bounds.left - pillBounds.left;
navBar.animate(
@ -64,23 +68,18 @@
easing: "ease-out",
},
{
transform: `translateX(${transformFactor / 100}px)`,
offset: 0.1,
transform: `translateX(${transformFactor / 50}px)`,
offset: 0.4,
easing: "ease-out",
},
{
transform: `translateX(${-transformFactor / 200}px)`,
offset: 0.8,
easing: "ease-in",
},
{
transform: "translateX(0px)",
easing: "ease-in",
},
],
{
duration: 500,
delay: 170,
duration: 360,
delay: 0,
},
);
}

View file

@ -52,12 +52,14 @@
stroke="white"
/>
</svg>
<h1 class="text-[36px] leading-[1.41] md:text-[60px] md:leading-[1.1]">
suyu Online Services
</h1>
<h2 class="text-[36px] leading-[1.41] md:text-[48px] md:leading-[1.1]">Multiplayer</h2>
<p class="text-wrap text-lg leading-relaxed text-[#A6A5A7]">
Your token should be kept private. If you believe it has been compromised, please
contact us immediately.
Hey, <span
class="bg-gradient-to-r from-[#60c7e9] via-[#e06bb3] to-[#f94d4d] bg-clip-text font-bold text-transparent"
>{data.user.username}</span
>! This is your <i>token</i>, used to authenticate your identity within suyu. Your token
should be kept private. If you believe it has been compromised, please contact us
immediately.
</p>
<div class="flex gap-4">
<div
@ -72,9 +74,5 @@
</div>
<button class="button-sm" on:click={copyToken}>{copyText}</button>
</div>
<div class="flex gap-4">
<a href="/account/friends" class="button-sm">Manage Friends</a>
<a href="/account/rooms" class="button-sm">Rooms</a>
</div>
</div>
</div>

View file

@ -0,0 +1,10 @@
<div class="relative h-[calc(100vh-200px)] flex-col gap-6 overflow-hidden">
<div
class="relative flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] bg-black p-8 md:p-12"
>
<h2 class=" text-[36px] leading-[1.41] md:text-[48px] md:leading-[1.1]">Friends</h2>
<p class="text-md text-wrap leading-relaxed text-[#4e4e4e]">
TBD: madeline has no friends :( ~maddie/null
</p>
</div>
</div>

View file

@ -0,0 +1,7 @@
import { RoomManager } from "$lib/server/class/Room.js";
export function load({ request }) {
return {
rooms: RoomManager.getRooms().map((r) => r.toJSON()),
};
}

View file

@ -1,7 +1,15 @@
<script lang="ts">
import type { PageData } from "./$types";
export let data: PageData;
</script>
<div class="relative h-[calc(100vh-200px)] flex-col gap-6 overflow-hidden">
<div
class="relative flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] bg-black p-8 md:p-12"
>
LOBBIES YEHAHH
{#each data.rooms as room}
<p>{room.name}</p>
{/each}
</div>
</div>

View file

@ -1,7 +0,0 @@
<div class="relative h-[calc(100vh-200px)] flex-col gap-6 overflow-hidden">
<div
class="relative flex w-full flex-col gap-6 overflow-hidden rounded-[2.25rem] bg-black p-8 md:p-12"
>
SETTTTTINGGGASSS WOOOOOOOOOOOO
</div>
</div>

View file

@ -39,7 +39,7 @@ export async function POST({ request, getClientAddress }) {
error: "missing fields",
});
}
if (body.username.length < 3 || body.username.length > 24) {
if (body.username.length < 3 || body.username.length > 24 || body.username.trim() === "") {
return json<CreateAccountResponse>({
success: false,
error: "invalid username",

View file

@ -36,9 +36,12 @@ export async function load({ request }) {
const user = await useModeratorAuth(request);
return {
posts,
userInfo: JSON.parse(JSON.stringify(user)) as {
userInfo: (JSON.parse(JSON.stringify(user)) as {
user: SuyuUser | null;
isModerator: boolean;
}) || {
user: null,
isModerator: false,
},
};
}

View file

@ -1,6 +1,9 @@
<script lang="ts">
import SvelteMarkdown from "svelte-markdown";
import CodeRenderer from "$components/CodeRenderer.svelte";
import type { PageData } from "./$types";
import { onMount } from "svelte";
import { goto } from "$app/navigation";
let content = "";
@ -8,6 +11,12 @@
event.preventDefault();
console.log(content);
};
export let data: PageData;
onMount(() => {
if (!data.userInfo.isModerator) goto("/blog");
});
</script>
<div class="flex h-[calc(100vh-196px)] min-h-[32rem] w-full flex-row gap-8">

View file

@ -0,0 +1,47 @@
import { json } from "$lib/server/util/index.js";
export function GET({ request }) {
return new Response(
`-----BEGIN CERTIFICATE-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
-----END CERTIFICATE-----`,
{
headers: {
"content-type": "text/plain",
},
},
);
}
export function POST({ request }) {
return new Response();
}

View file

@ -0,0 +1,16 @@
import { PRIVATE_KEY } from "$lib/server/secrets/secrets.json";
import { useAuth } from "$lib/util/api/index.js";
import jwt from "jsonwebtoken";
export async function POST({ request }) {
const userKey = `${request.headers.get("x-username")}:${request.headers.get("x-token")}`;
const user = await useAuth(userKey);
const token = jwt.sign({ ...user, apiKey: userKey }, Buffer.from(PRIVATE_KEY), {
algorithm: "RS256",
});
return new Response(token, {
headers: {
"content-type": "text/html",
},
});
}

View file

@ -0,0 +1,65 @@
import { Room, RoomManager } from "$lib/server/class/Room";
import { userRepo } from "$lib/server/repo/index.js";
import { SuyuUser } from "$lib/server/schema";
import { PUBLIC_KEY } from "$lib/server/secrets/secrets.json";
import { json } from "$lib/server/util";
import { useAuth } from "$lib/util/api/index.js";
import type { IJwtData } from "$types/auth.js";
import type { IRoom, LobbyResponse } from "$types/rooms";
import jwt from "jsonwebtoken";
export async function GET({ request }) {
return json<LobbyResponse>({
rooms: RoomManager.getRooms().map((r) => r.toJSON()),
});
}
/* credit to janeberru for showing the shape of this data */
export async function POST({ request, getClientAddress }) {
// TODO: per-ip room limit
const body: IRoom = await request.json();
/* description may contain "### END DESCRIPTION ###" on its own line. if it does, get all lines after that */
const parsedDescription = body.description.split("### END DESCRIPTION ###");
console.log(parsedDescription);
const description = parsedDescription?.slice(1)?.join("### END DESCRIPTION ###") || "";
const opts: { [key: string]: string } = {};
description.split("\n").forEach((line) => {
const [key, ...values] = line.split("=");
const value = values.join("=").trim();
if (!key || !value) return;
opts[key] = value;
});
if (opts.ip) {
if (
!opts.ip.match(
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
)
) {
return new Response(null, { status: 400 });
}
}
const token = request.headers.get("authorization");
if (!token) return new Response(null, { status: 401 });
// TODO: jwt utils which type and validate automatically
const user = await useAuth(token);
console.log(user);
if (!user) return new Response(null, { status: 401 });
const room = RoomManager.createRoom({
name: body.name,
description: parsedDescription[0] || "",
gameName: body.preferredGameName,
gameId: body.preferredGameId,
players: [
{
gameId: 0,
gameName: "",
nickname: user.username,
},
],
maxPlayers: body.maxPlayers,
ip: `${opts.ip || getClientAddress().split(":").at(-1)}:${body.port}`,
host: user,
hasPassword: body.hasPassword || false,
});
return json(room.toJSON());
}

View file

@ -0,0 +1,30 @@
import { RoomManager } from "$lib/server/class/Room";
import { json } from "$lib/server/util/index.js";
import { useAuth } from "$lib/util/api/index.js";
/* thanks again janeberru for the shape of this data */
export async function POST({ request, params }) {
const body = await request.json();
const { id } = params;
const room = RoomManager.getRoom(id);
if (!room) return new Response(null, { status: 500 });
const user = await useAuth(request.headers.get("authorization") || "");
if (!user) return new Response(null, { status: 401 });
if (user.id !== room.host.id) return new Response(null, { status: 401 });
if (body.players.length === 0 && room.roomInfo.owner) {
console.log(room.roomInfo.players);
room.setPlayerList([{ gameId: 0, gameName: "", nickname: room.roomInfo.owner }]);
}
return json({ message: "Lobby updated successfully" });
}
export async function DELETE({ request, params }) {
const { id } = params;
const room = RoomManager.getRoom(id);
if (!room) return new Response(null, { status: 500 });
const user = await useAuth(request.headers.get("authorization") || "");
if (!user) return new Response(null, { status: 401 });
if (user.id !== room.host.id) return new Response(null, { status: 401 });
room.delete();
return json(room.toJSON());
}

View file

@ -0,0 +1,11 @@
import { json } from "$lib/server/util/index";
import { useAuth } from "$lib/util/api/index.js";
export async function GET({ request }) {
const user = await useAuth(request.headers.get("authorization") || "");
console.log(user);
if (!user) return new Response(null, { status: 401 });
return json({
username: user.username,
});
}