remove react

This commit is contained in:
darwinx64 2025-04-20 23:52:17 -05:00
parent 98412adab9
commit 0bb40ff89f
No known key found for this signature in database
29 changed files with 430 additions and 1730 deletions

View file

@ -0,0 +1,52 @@
---
import { Image } from "astro:assets"
import logo from "@assets/logo.png"
---
<div class="avatar-wrapper">
<Image
alt="My profile picture"
src={logo}
class="avatar"
width={80}
height={80}
aria-hidden
/>
</div>
<style>
.avatar-wrapper {
width: 80px;
height: 80px;
clip-path: var(--clip-avatar-outer);
background-color: var(--overlay0);
}
.avatar {
width: 80px;
height: 80px;
clip-path: var(--clip-avatar-inner);
}
</style>
<script>
const colours = {
"online": "var(--green)",
"idle": "var(--yellow)",
"dnd": "var(--red)",
"offline": "var(--overlay0)"
};
const avatarWrapper: HTMLElement = document.querySelector(".avatar-wrapper");
window.addEventListener('lanyard-update', (e: CustomEvent) => {
const presence = e.detail;
avatarWrapper.animate(
[
{ backgroundColor: colours[presence.discord_status] ?? colours.offline }
],
{
duration: 100,
fill: "forwards"
},
);
});
</script>

View file

@ -2,7 +2,6 @@
const { ...attrs } = Astro.props
---
<a {...attrs}>
<slot name="icon" />
<slot />
</a>
<style>

View file

@ -1,7 +1,3 @@
---
const { name } = Astro.props;
---
<style>
.group-box-wrapper {
background: linear-gradient(#343343, #2E2E3E, #2E2E3E);
@ -19,7 +15,6 @@ const { name } = Astro.props;
<div class="group-box-wrapper">
<div class="group-box">
<h3>{name}</h3>
<slot />
</div>
</div>

View file

@ -1,20 +1,11 @@
<style>
div {
color: black;
background-color: lightcoral;
padding: 10px;
margin-bottom: 20px;
border-radius: 10px;
}
a {
color: blue;
}
</style>
---
import GroupBox from "@components/GroupBox.astro";
---
<noscript>
<div>
<GroupBox>
<p>
<h4>ENABLE JAVASCRIPT OR I WILL KILL YOU</h4>
<h2>ENABLE JAVASCRIPT OR I WILL KILL YOU</h2>
I will not. However, you seem to be living in fear of technology, as
you have JavaScript disabled. This means that some features (eg. dragging,
submitting forms) will not work. Enable JS to fully enjoy this website!
@ -24,5 +15,5 @@
>nin0/website</a
> repository on my Forgejo.
</p>
</div>
</GroupBox>
</noscript>

View file

@ -1,16 +1,16 @@
---
import Chip from "./Chip.astro";
import Chip from "@components/Chip.astro";
import Envelope from "@assets/svg/envelope.svg";
import Plane from "@assets/svg/plane.svg";
---
<div>
<Chip href="mailto:support@nin0.dev">
<Envelope slot="icon" />
<Envelope />
support@nin0.dev
</Chip>
<Chip href="https://nin0dev.t.me">
<Plane slot="icon" />
<Plane />
@nin0dev
</Chip>
</div>

View file

@ -0,0 +1,26 @@
---
import Avatar from "components/Avatar.astro"
import SpotifyCard from "./SpotifyCard.astro"
---
<>
<div class="avatar-stack">
<Avatar />
<div class="text-stack">
<h2>nin0</h2>
<sub>he/him</sub>
</div>
</div>
<SpotifyCard />
</>
<style>
.avatar-stack {
display: flex;
gap: 12px;
align-items: center;
}
.text-stack {
display: flex;
flex-direction: column;
}
</style>

View file

@ -1,29 +1,42 @@
---
import Contacts from "./Contacts.astro";
import GroupBox from "@components/GroupBox.astro";
import Me from "./Me";
import InfoStack from "./InfoStack.astro";
import Window from "@components/Window.astro";
import Button from "@components/Button.astro";
import GroupBox from "@components/GroupBox.astro";
---
<script>
import { WindowManager } from "@models/WindowManager";
import { Lanyard } from "@models/Lanyard";
import { LANYARD_ID } from "utils/constants";
document
.querySelector("#show-code-window")
.addEventListener("click", () => WindowManager.topmost = document.getElementById("window-code-window"));
new Lanyard(LANYARD_ID, presence =>
window.dispatchEvent(new CustomEvent('lanyard-update', {
detail: presence
}))
);
</script>
<Window title="Home" maxWidth="600px" showClose={false}>
<Me client:only />
<InfoStack />
<Button position="left" id="show-code-window">My code</Button>
<GroupBox name="About me">
<GroupBox>
<h3>About me</h3>
<p>
I'm a Canadian self-taught software developer.
I also make things that some people use with varying degrees of usefulness.
</p>
</GroupBox>
<GroupBox name="Reach out">
<GroupBox>
<h3>Reach out</h3>
<Contacts />
</GroupBox>
</Window>

View file

@ -0,0 +1,184 @@
---
import LastFMIcon from "@assets/svg/fm.svg"
---
<div class="spotify-card-wrapper-shadow">
<div class="spotify-card-wrapper">
<div class="spotify-card">
<svg aria-hidden="true" class="blur">
<filter id='sharpBlur' color-interpolation-filters="sRGB">
<feGaussianBlur stdDeviation='100'></feGaussianBlur>
<feColorMatrix type='matrix' values='1 0 0 0 0, 0 1 0 0 0, 0 0 1 0 0, 0 0 0 9 0'></feColorMatrix>
<feComposite in2='SourceGraphic' operator='in'></feComposite>
</filter>
</svg>
<div class="spotify-card-background"></div>
<div class="spotify-card-art-shadow">
<div class="spotify-card-art"></div>
</div>
<div class="spotfiy-card-track-info">
<div class="spotify-card-track-name"></div>
<div class="spotify-card-track-artist"></div>
<div class="spotify-card-track-album"></div>
</div>
<div class="links">
<a class="lastfm-link" href="https://www.last.fm/user/nin0dev"><LastFMIcon /></a>
</div>
</div>
</div>
</div>
<style>
.blur {
display: none;
}
.spotify-card-wrapper-shadow {
display: none;
filter: drop-shadow(0 0 5px #00000040);
}
.spotify-card-wrapper {
position: relative;
display: flex;
background: var(--album-color);
clip-path: var(--clip-sp-1);
}
.spotify-card {
display: flex;
flex: 1;
gap: 12px;
padding: 15px;
align-items: center;
clip-path: var(--clip-sp-2);
}
.spotify-card-background {
position: absolute;
left: 0;
top: 0;
width: 105%;
height: 105%;
background: var(--album-art);
background-size: 100% 100%;
filter: url("#sharpBlur") brightness(0.75);
z-index: -1;
}
.spotify-card-art-shadow {
filter: drop-shadow(0 0 5px #00000040);
}
.spotify-card-art-wrapper {
clip-path: var(--clip-sp-art-wrapper);
background: var(--album-color);
}
.spotify-card-art {
background-image: var(--album-art);
background-size: 100%;
width: 4rem;
height: 4rem;
clip-path: var(--clip-sp-art);
}
.spotfiy-card-track-info {
display: flex;
flex-direction: column;
line-height: 1;
}
.spotify-card-track-name {
margin-bottom: 5px;
}
.spotify-card-track-name::after {
content: var(--track);
font-weight: 500;
font-size: 1.05rem;
color: #ffffffd5;
filter: drop-shadow(0 0 5px #0000006d);
}
.spotify-card-track-artist::after, .spotify-card-track-album::after {
color: #ffffffa1;
font-weight: 500;
font-size: 0.85rem;
filter: drop-shadow(0 0 5px #00000040);
}
.spotify-card-track-artist::after {
content: var(--artist);
}
.spotify-card-track-album::after {
content: var(--album);
}
.links {
position: absolute;
bottom: 0;
right: 0;
margin: 10px;
display: flex;
gap: 7px;
}
.links a {
color: rgba(255, 255, 255, 0.516);
text-decoration: none;
}
</style>
<script>
import { mergeStyles } from "utils/mergeStyles";
import { average } from "color.js";
import { LASTFM_KEY, LASTFM_API } from "utils/constants";
const spotifyCardWrapper: HTMLElement = document.querySelector(".spotify-card-wrapper-shadow");
async function getLastFmTracks() {
const params = new URLSearchParams({
method: "user.getrecenttracks",
api_key: LASTFM_KEY,
user: "nin0dev",
limit: "50",
format: "json",
extended: "true"
});
const response = await(
await fetch(`${LASTFM_API}${params}`)
).json();
// TODO: make lastfm stuff typed
if (
response.recenttracks.track.some(t => {
try {
return t["@attr"].nowplaying === "true";
} catch (e) {
return false;
}
})
) {
const track = response.recenttracks.track[0];
mergeStyles({
"display": "block",
"--album-art": `url("${track.image[1]["#text"]}")`,
"--track": `'${track.name}'`,
"--album": `'${track.album["#text"]}'`,
"--album-color": await average(
`${track.image[1]["#text"]}`,
{
format: "hex"
}
),
"--artist": `'${track.artist.name}'`
}, spotifyCardWrapper.style);
(document.querySelector(".lastfm-link") as HTMLLinkElement).href = track.url;
}
}
getLastFmTracks();
setInterval(getLastFmTracks, 10000);
</script>

View file

@ -1,245 +0,0 @@
import "@css/me.css";
import type { LanyardPresence } from "types/lanyard";
import { Lanyard } from "models/Lanyard";
import React, { useEffect, useState } from "react";
import { average } from "color.js";
const lightenColor = (color: string, percent: number) =>
`hsl(from ${color} h s ${percent.toString()}%)`;
const apiKey = "3c5623fa1abbd11c49f53ca18e992ead";
export default function Me() {
// me-e-eeeee
const [presence, setPresence] = useState<LanyardPresence | undefined>();
const [currentlyPlaying, setCurrentlyPlaying] = useState<
| {
title: string;
artist: string;
album: string;
albumArt: string;
mainColor: string;
loved: boolean;
streak: number;
spotifyURL?: string;
}
| undefined
>();
useEffect(() => {
new Lanyard("886685857560539176", p => setPresence(p));
async function getLastFmTracks() {
const params = new URLSearchParams({
method: "user.getrecenttracks",
api_key: apiKey,
user: "nin0dev",
limit: "50",
format: "json",
extended: "true"
});
const res = await (
await fetch(`https://ws.audioscrobbler.com/2.0/?${params}`)
).json();
if (
res.recenttracks.track.some(t => {
try {
return t["@attr"].nowplaying === "true";
} catch (e) {
return false;
}
})
) {
const currentlyPlayingTrack = res.recenttracks.track[0];
const trackExtra = (
await (
await fetch(
`https://ws.audioscrobbler.com/2.0/?${new URLSearchParams(
{
method: "track.getinfo",
api_key: apiKey,
format: "json",
track: currentlyPlayingTrack.name,
artist: currentlyPlayingTrack.artist.name
}
)}`
)
).json()
).track;
setCurrentlyPlaying({
title: currentlyPlayingTrack.name,
artist: currentlyPlayingTrack.artist.name,
album: currentlyPlayingTrack.album["#text"],
albumArt: currentlyPlayingTrack.image[1]["#text"],
// @ts-ignore this library is poorly typed
mainColor: await average(
currentlyPlayingTrack.image[1]["#text"],
{
format: "hex"
}
),
loved: currentlyPlayingTrack.loved === "1",
streak: (() => {
let current = 0;
for (const track of res.recenttracks.track) {
if (track.name === currentlyPlayingTrack.name)
current++;
else return current;
}
})()
});
} else {
setCurrentlyPlaying(undefined);
}
}
getLastFmTracks();
setInterval(getLastFmTracks, 10000);
}, []);
return (
<>
<div className="me-container">
<div
className="img-container"
style={{
backgroundColor: presence
? ["online", "idle"].includes(
presence.discord_status
)
? "var(--green)"
: "var(--overlay0)"
: "var(--overlay0)"
}}
>
<img
src="logo.png"
alt="the nin0 logo"
/>
</div>
<div>
<h2>
nin0
</h2>
<h4>
he/him
</h4>
</div>
</div>
{currentlyPlaying && (
<div
className="spotify-card-wrapper"
style={{
"--album-color": currentlyPlaying.mainColor,
"--album-art": `url(${currentlyPlaying.albumArt})`
} as React.CSSProperties}
>
<div
className="spotify-card"
>
<h3
style={{
color: lightenColor(currentlyPlaying.mainColor, 90)
}}
>
Listening to
</h3>
<div
className="spotify-card-content"
style={{
display: "flex",
alignItems: "center",
marginBottom: "10px"
}}
>
<img
src={currentlyPlaying.albumArt}
style={{
marginRight: "10px",
width: "50px",
borderRadius: "5px"
}}
/>
<div>
<p
style={{
margin: "0",
fontWeight: "600",
color: lightenColor(
currentlyPlaying.mainColor,
90
)
}}
>
{currentlyPlaying.title}
</p>
<p
style={{
margin: "0",
color: lightenColor(
currentlyPlaying.mainColor,
85
)
}}
className="secondary-meta"
>
{currentlyPlaying.artist}
</p>
<p
style={{
margin: "0",
color: lightenColor(
currentlyPlaying.mainColor,
85
)
}}
className="secondary-meta"
>
{currentlyPlaying.album}
</p>
</div>
</div>
<p style={{ fontSize: "0.9rem" }} className="emoji">
{(() => {
const flags: {
color: string;
data: string;
}[] = [];
if (currentlyPlaying.loved)
flags.push({
color: "#f38ba8",
data: "💞 Loved track"
});
if (currentlyPlaying.streak > 1) {
flags.push({
color: "#f9e2af",
data: `⚡ Played ${currentlyPlaying.streak} times in a row`
});
}
return flags.map(flag => (
<span
style={{ color: flag.color }}
className="emoji"
key={flag.color}
>
{flag.data}{" "}
{flags.indexOf(flag) !==
flags.length - 1 && (
<span style={{ color: "white" }}>
{" "}{" "}
</span>
)}
</span>
));
})()}
</p>
</div>
</div>
)}
</>
);
}