feat: website

This commit is contained in:
trafficlunar 2025-05-03 17:40:21 +01:00
parent fc22cf0330
commit 215e6944a8
45 changed files with 709 additions and 16 deletions

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { onMount } from "svelte";
let clock = $state("");
let date = $state("");
const myTimezone = "Europe/London";
onMount(() => {
const interval = setInterval(() => {
const dateObject = new Date();
clock = dateObject.toLocaleTimeString("en-US", { timeZone: myTimezone, hour: "2-digit", minute: "2-digit", second: "2-digit" }).toLowerCase();
date = dateObject.toLocaleDateString("en-GB", { timeZone: myTimezone, day: "numeric", month: "short" });
}, 100);
return () => clearInterval(interval);
});
</script>
<section class="font-mono !py-2.5">
<legend>clock</legend>
<span class="block font-bold text-lg">{clock}</span>
<span class="block text-xs">{date}</span>
</section>

View file

@ -0,0 +1,12 @@
---
import child_process from "child_process";
// Get commit hash
const hash = child_process.execSync("git rev-parse --short HEAD").toString().trim();
---
<footer class="flex justify-between gap-3 text-peach/50 *:hover:text-peach *:transition-colors text-sm mt-4">
<a href="https://github.com/trafficlunar/website">Source code</a>
<!-- <span>•</span> -->
<a href=`https://github.com/trafficlunar/website/commit/${hash}` class="font-mono">{hash}</a>
</footer>

View file

@ -0,0 +1,57 @@
<script lang="ts">
import { onMount } from "svelte";
let song = $state("loading...");
let artist = $state("...");
let image = $state("/missing.webp");
let url = $state("");
let playing = $state(false);
onMount(() => {
const get = async () => {
const request = await fetch("https://api.trafficlunar.net/song");
const data = await request.json();
song = data.song;
artist = data.artist;
image = data.image;
url = data.url;
playing = data.playing;
};
get();
const interval = setInterval(get, 30000);
return () => clearInterval(interval);
});
</script>
<a href={url} class="block relative h-20 group">
<div class="absolute size-full rounded-md flex gap-2 bg-mantle border border-surface0 z-10"></div>
<div class="flex justify-center items-center absolute -top-2 right-2 z-0">
<div class="w-4 h-4 rounded-full {playing ? 'bg-green' : 'bg-red'}"></div>
<div class="w-4 h-4 rounded-full absolute animate-duration-2s animate-delay-2s {playing ? 'bg-green animate-ping' : 'bg-red'}"></div>
</div>
<div class="absolute p-2 flex gap-2 z-30 w-full">
<img src={image} alt="album cover" class="size-16 rounded-full animate-spin animate-duration-30s" />
<div class="flex flex-col w-full min-w-0">
<h1 class="font-medium">{song}</h1>
<span class="text-xs">{artist}</span>
</div>
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="22"
height="22"
viewBox="0 0 20 20"
class="absolute right-2 top-2 z-10 text-overlay1 transition-all group-hover:top-1.5 group-hover:right-1.5 group-hover:text-peach"
><path
fill="currentColor"
d="M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v7.5c0 .966.784 1.75 1.75 1.75h7.5a1.75 1.75 0 0 0 1.75-1.75v-2a.75.75 0 0 1 1.5 0v2A3.25 3.25 0 0 1 13.75 17h-7.5A3.25 3.25 0 0 1 3 13.75v-7.5A3.25 3.25 0 0 1 6.25 3h2a.75.75 0 0 1 0 1.5zm4.25-.75a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 .75.75v5a.75.75 0 0 1-1.5 0V5.56l-3.72 3.72a.75.75 0 1 1-1.06-1.06l3.72-3.72h-3.19a.75.75 0 0 1-.75-.75"
/></svg
>
</a>

View file

@ -0,0 +1,75 @@
<script lang="ts">
import { onMount } from "svelte";
interface Project {
owner: string;
name: string;
description: string;
stars: number;
language: string;
url: string;
color?: string;
}
const languageColors: Record<string, string> = {
JavaScript: "bg-yellow",
TypeScript: "bg-blue",
Python: "bg-[#3572A5]",
Java: "bg-peach",
Rust: "bg-[#dea584]",
C: "bg-[#555555]",
"C++": "bg-[#f34b7d]",
Go: "bg-sapphire",
Astro: "bg-[#ff5a03]",
Svelte: "bg-[#ff3e00]",
};
let projects: Project[] = [];
onMount(async () => {
const response = await fetch("https://api.trafficlunar.net/projects");
const data: Project[] = await response.json();
projects = data.map((project) => ({ ...project, color: languageColors[project.language ?? ""] ?? "bg-overlay0" }));
});
</script>
<div class="grid grid-cols-2 gap-4">
{#each projects as project}
<a href={project.url} class="group">
<section class="h-full flex flex-col">
<h1 class="font-medium text-xl mb-1 text-nowrap overflow-hidden text-ellipsis w-11/12" title={project.name}>{project.name}</h1>
<p class="mb-4 text-sm text-subtext0">{project.description}</p>
<div class="text-sm flex gap-6 mt-auto">
<div class="flex items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" class="text-yellow"
><path
fill="currentColor"
d="m12 17.275l-4.15 2.5q-.275.175-.575.15t-.525-.2t-.35-.437t-.05-.588l1.1-4.725L3.775 10.8q-.25-.225-.312-.513t.037-.562t.3-.45t.55-.225l4.85-.425l1.875-4.45q.125-.3.388-.45t.537-.15t.537.15t.388.45l1.875 4.45l4.85.425q.35.05.55.225t.3.45t.038.563t-.313.512l-3.675 3.175l1.1 4.725q.075.325-.05.588t-.35.437t-.525.2t-.575-.15z"
/></svg
>
<span>{project.stars}</span>
</div>
<div class="flex items-center gap-1.5">
<div class={`size-4 rounded-full ${project.color}`}></div>
<span>{project.language}</span>
</div>
</div>
<svg
xmlns="http://www.w3.org/2000/svg"
width="22"
height="22"
viewBox="0 0 20 20"
class="absolute right-4 top-4 text-overlay1 transition-all group-hover:top-3.5 group-hover:right-3.5 group-hover:text-peach"
><path
fill="currentColor"
d="M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v7.5c0 .966.784 1.75 1.75 1.75h7.5a1.75 1.75 0 0 0 1.75-1.75v-2a.75.75 0 0 1 1.5 0v2A3.25 3.25 0 0 1 13.75 17h-7.5A3.25 3.25 0 0 1 3 13.75v-7.5A3.25 3.25 0 0 1 6.25 3h2a.75.75 0 0 1 0 1.5zm4.25-.75a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 .75.75v5a.75.75 0 0 1-1.5 0V5.56l-3.72 3.72a.75.75 0 1 1-1.06-1.06l3.72-3.72h-3.19a.75.75 0 0 1-.75-.75"
/></svg
>
</section>
</a>
{/each}
</div>

View file

@ -0,0 +1,40 @@
---
import GitHubIcon from "../assets/icons/socials/github.svg";
import BlueskyIcon from "../assets/icons/socials/bluesky.svg";
import RedditIcon from "../assets/icons/socials/reddit.svg";
import DiscordIcon from "../assets/icons/socials/discord.svg";
import YouTubeIcon from "../assets/icons/socials/youtube.svg";
import MyAnimeListIcon from "../assets/icons/socials/myanimelist.svg";
import LastFMIcon from "../assets/icons/socials/lastfm.svg";
import MailIcon from "../assets/icons/socials/mail.svg";
---
<section class="mt-8 !p-2">
<legend>socials</legend>
<ul class="grid grid-cols-4 [&_a]:flex [&_a]:justify-center [&_a]:size-full [&_a]:py-1">
<li class="z-50">
<a href="https://github.com/trafficlunar" data-tooltip="GitHub"><GitHubIcon /></a>
</li>
<li class="z-50">
<a href="https://bsky.app/profile/trafficlunar.net" data-tooltip="Bluesky"><BlueskyIcon /></a>
</li>
<li class="z-50">
<a href="https://www.reddit.com/user/trafficlunr" data-tooltip="Reddit"><RedditIcon /></a>
</li>
<li class="z-50">
<a href="https://discord.com/users/1076507715775496233" data-tooltip="Discord"><DiscordIcon /></a>
</li>
<li>
<a href="https://www.youtube.com/@trafficlunar" data-tooltip="YouTube"><YouTubeIcon /></a>
</li>
<li>
<a href="https://myanimelist.net/profile/trafficlunar" data-tooltip="MyAnimeList"><MyAnimeListIcon /></a>
</li>
<li>
<a href="https://www.last.fm/user/axolotlmaid" data-tooltip="Last.FM"><LastFMIcon /></a>
</li>
<li>
<a href="mailto:hello@trafficlunar.net" data-tooltip="E-mail"><MailIcon /></a>
</li>
</ul>
</section>

View file

@ -0,0 +1,95 @@
---
import VSCodiumIcon from "../assets/icons/coding/vscodium.svg";
import IntelliJIdeaIcon from "../assets/icons/coding/intellij.svg";
import TypeScriptIcon from "../assets/icons/coding/typescript.svg";
import GoIcon from "../assets/icons/coding/go.svg";
import RustIcon from "../assets/icons/coding/rust.svg";
import LuaUIcon from "../assets/icons/coding/lua.svg";
import JavaIcon from "../assets/icons/coding/java.svg";
import PythonIcon from "../assets/icons/coding/python.svg";
import SvelteIcon from "../assets/icons/coding/svelte.svg";
import ReactIcon from "../assets/icons/coding/react.svg";
import NextIcon from "../assets/icons/coding/next.svg";
import AstroIcon from "../assets/icons/coding/astro.svg";
---
<section class="!mb-0">
<legend>tools</legend>
<!-- separator -->
<div class="flex items-center gap-4 text-subtext0 text-sm font-medium mb-2">
<hr class="w-4 border-surface1" />
<span>editors</span>
<hr class="flex-grow border-surface1" />
</div>
<div class="flex gap-1 mb-4 *:bg-surface0/50 *:border *:border-surface1 *:rounded-md *:p-1.5 *:text-sm *:flex *:items-center *:gap-1.5">
<div>
<VSCodiumIcon class="size-5" />
<span>VSCodium</span>
</div>
<div>
<IntelliJIdeaIcon class="size-5" />
<span>IntelliJ IDEA</span>
</div>
</div>
<!-- separator -->
<div class="flex items-center gap-4 text-subtext0 text-sm font-medium my-2">
<hr class="w-4 border-surface1" />
<span>languages</span>
<hr class="flex-grow border-surface1" />
</div>
<div class="flex gap-1 mb-4 *:bg-surface0/50 *:border *:border-surface1 *:rounded-md *:p-1.5 *:text-sm *:flex *:items-center *:gap-1.5">
<div>
<TypeScriptIcon class="size-5" />
<span>TypeScript</span>
</div>
<div>
<GoIcon class="size-5" />
<span>Go</span>
</div>
<div>
<RustIcon class="size-5" />
<span>Rust</span>
</div>
<div data-tooltip="The Roblox version">
<LuaUIcon class="size-5" />
<span>Luau</span>
</div>
<div>
<JavaIcon class="size-5" />
<span>Java</span>
</div>
<div>
<PythonIcon class="size-5" />
<span>Python</span>
</div>
</div>
<!-- separator -->
<div class="flex items-center gap-4 text-subtext0 text-sm font-medium my-2">
<hr class="w-4 border-surface1" />
<span>frameworks</span>
<hr class="flex-grow border-surface1" />
</div>
<div class="flex gap-1 *:bg-surface0/50 *:border *:border-surface1 *:rounded-md *:p-1.5 *:text-sm *:flex *:items-center *:gap-1.5">
<div>
<SvelteIcon class="size-5" />
<span>Svelte</span>
</div>
<div>
<ReactIcon class="size-5" />
<span>React</span>
</div>
<div>
<NextIcon class="size-5" />
<span>Next</span>
</div>
<div>
<AstroIcon class="size-5" />
<span>Astro</span>
</div>
</div>
</section>