remove react
This commit is contained in:
parent
98412adab9
commit
0bb40ff89f
29 changed files with 430 additions and 1730 deletions
|
@ -1,25 +1,3 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import node from "@astrojs/node";
|
||||
|
||||
import cloudflare from "@astrojs/cloudflare";
|
||||
|
||||
import react from "@astrojs/react";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
output: "server",
|
||||
adapter: cloudflare(),
|
||||
integrations: [react()],
|
||||
vite: {
|
||||
server: {
|
||||
allowedHosts: ["nin0-pc.buri-roach.ts.net"]
|
||||
},
|
||||
resolve: {
|
||||
// Use react-dom/server.edge instead of react-dom/server.browser for React 19.
|
||||
// Without this, MessageChannel from node:worker_threads needs to be polyfilled.
|
||||
alias: import.meta.env.PROD && {
|
||||
"react-dom/server": "react-dom/server.edge"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
export default defineConfig({});
|
15
package.json
15
package.json
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"name": "",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
|
@ -10,21 +9,11 @@
|
|||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "^12.5.0",
|
||||
"@astrojs/node": "^9.2.0",
|
||||
"@astrojs/react": "^4.2.4",
|
||||
"@fontsource-variable/noto-emoji": "^5.2.5",
|
||||
"@fontsource/inter": "^5.2.5",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"astro": "^5.7.4",
|
||||
"color.js": "^1.2.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
"color.js": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-astro": "^0.14.1",
|
||||
"wrangler": "^3.114.6"
|
||||
"prettier-plugin-astro": "^0.14.1"
|
||||
}
|
||||
}
|
1393
pnpm-lock.yaml
generated
1393
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
BIN
public/1024x1024.png
Normal file
BIN
public/1024x1024.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 MiB |
BIN
public/fonts/Inter/Inter Italic.ttf
Normal file
BIN
public/fonts/Inter/Inter Italic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Inter/Inter.ttf
Normal file
BIN
public/fonts/Inter/Inter.ttf
Normal file
Binary file not shown.
93
public/fonts/Inter/OFL.txt
Normal file
93
public/fonts/Inter/OFL.txt
Normal file
|
@ -0,0 +1,93 @@
|
|||
Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
5
src/assets/svg/fm.svg
Normal file
5
src/assets/svg/fm.svg
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated by Pixelmator Pro 3.3.8 -->
|
||||
<svg width="16" viewBox="0 0 293 179" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" stroke="none" d="M -0.282013 158.117004 C -0.282013 168.822006 8.071991 177.434998 18.773987 177.434998 C 30 177.434998 38.35199 168.822006 38.35199 158.117004 C 38.35199 147.153992 29.998993 138.804001 18.773987 138.804001 C 8.071991 138.804001 -0.282013 147.153992 -0.282013 158.117004 Z M 52.72699 76.673996 L 52.72699 175.869003 L 80.135986 175.869003 L 80.135986 76.673996 L 110.939026 76.673996 L 110.939026 55.271004 L 80.135986 55.271004 L 80.135986 44.306 C 80.135986 27.862 87.184998 22.641006 98.669983 22.641006 C 106.762024 22.641006 112.244019 24.466003 118.508972 27.862 L 122.945984 4.888 C 115.638 1.494995 107.02301 -0.332001 96.582001 -0.332001 C 73.609009 -0.332001 52.72699 10.630997 52.72699 43.261002 L 52.72699 55.271004 L 35.238007 55.271004 L 35.238007 76.673996 Z M 206.153992 79.026001 C 203.020996 59.447998 190.231018 52.397003 173.523987 52.397003 C 156.817993 52.397003 142.461975 59.968002 136.195007 78.500999 L 132.802002 55.271004 L 110.614014 55.271004 L 110.614014 175.869003 L 138.02301 175.869003 L 138.02301 107.739998 C 138.02301 84.504997 150.031006 75.629997 162.822021 75.629997 C 176.133972 75.629997 181.617004 84.504997 181.617004 98.862 L 181.617004 175.867996 L 208.763977 175.867996 L 208.763977 107.477997 C 208.763977 84.503998 221.03302 75.628998 233.825012 75.628998 C 246.877014 75.628998 252.356995 84.503998 252.356995 98.861 L 252.356995 175.867004 L 279.765991 175.867004 L 279.765991 89.207001 C 279.765991 63.363998 264.625977 52.397003 244.526001 52.397003 C 227.560974 52.397003 212.419006 59.968002 206.153992 79.026001 Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
52
src/components/Avatar.astro
Normal file
52
src/components/Avatar.astro
Normal 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>
|
|
@ -2,7 +2,6 @@
|
|||
const { ...attrs } = Astro.props
|
||||
---
|
||||
<a {...attrs}>
|
||||
<slot name="icon" />
|
||||
<slot />
|
||||
</a>
|
||||
<style>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
26
src/components/windows/main/InfoStack.astro
Normal file
26
src/components/windows/main/InfoStack.astro
Normal 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>
|
|
@ -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>
|
184
src/components/windows/main/SpotifyCard.astro
Normal file
184
src/components/windows/main/SpotifyCard.astro
Normal 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>
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1,15 +1,19 @@
|
|||
@import "@css/catppuccin.css";
|
||||
@import "@css/clip-paths.css";
|
||||
@import "@css/inter.css";
|
||||
|
||||
:root {
|
||||
--background: url("/background.png");
|
||||
background: url("/background.png");
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
* {
|
||||
font-family: BlinkMacSystemFont, Inter, sans-serif;
|
||||
font-family: BlinkMacSystemFont, "Inter", sans-serif;
|
||||
}
|
||||
body {
|
||||
background: var(--background);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 300;
|
||||
color: var(--text);
|
||||
padding: 30px;
|
||||
overflow: hidden;
|
||||
|
@ -17,17 +21,9 @@ body {
|
|||
a {
|
||||
color: #89b4fa;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
body {
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
||||
.emoji {
|
||||
font-family: "Inter", "Noto Emoji Variable";
|
||||
}
|
||||
h1,h2,h3,h4,h5,h6,p,ul {
|
||||
margin: 0;
|
||||
}
|
||||
h4,h5,h6 {
|
||||
h4,h5,h6,sub {
|
||||
color: var(--overlay0)
|
||||
}
|
||||
|
|
11
src/css/inter.css
Normal file
11
src/css/inter.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: normal;
|
||||
src: url("/fonts/Inter/Inter.ttf");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Inter";
|
||||
font-style: italic;
|
||||
src: url("/fonts/Inter/Inter Italic.ttf");
|
||||
}
|
|
@ -23,6 +23,9 @@
|
|||
font-size: 0.85rem;
|
||||
}
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
clip-path: var(--clip-sp-2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
---
|
||||
import "@css/global.css";
|
||||
import "@css/clip-paths.css";
|
||||
import "@fontsource/inter";
|
||||
import "@fontsource-variable/noto-emoji";
|
||||
|
||||
const { tabTitle } = Astro.props;
|
||||
---
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
---
|
||||
import FearOfTechnology from "@windows/mainWindow/FearOfTechnology.astro";
|
||||
import MainWindow from "@windows/mainWindow/MainWindow.astro";
|
||||
import Noscript from "@components/Noscript.astro";
|
||||
import MainWindow from "@components/windows/main/MainWindow.astro";
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
import CodeWindow from "@windows/code/CodeWindow.astro";
|
||||
import CodeWindow from "@components/windows/code/CodeWindow.astro";
|
||||
---
|
||||
|
||||
<BaseLayout tabTitle="nin0.dev">
|
||||
<FearOfTechnology />
|
||||
<Noscript />
|
||||
<MainWindow />
|
||||
<CodeWindow />
|
||||
</BaseLayout>
|
||||
|
|
4
src/utils/constants.ts
Normal file
4
src/utils/constants.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export const LASTFM_KEY: string = "3c5623fa1abbd11c49f53ca18e992ead";
|
||||
export const LASTFM_API: string = "https://ws.audioscrobbler.com/2.0/?";
|
||||
export const LANYARD_ID: string = "886685857560539176";
|
||||
export const LANYARD_API: string = "wss://api.lanyard.rest/socket";
|
5
src/utils/mergeStyles.ts
Normal file
5
src/utils/mergeStyles.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export function mergeStyles(source, target: CSSStyleDeclaration) {
|
||||
for (const prop in source) {
|
||||
target.setProperty(prop, source[prop])
|
||||
}
|
||||
}
|
|
@ -3,15 +3,11 @@
|
|||
"compilerOptions": {
|
||||
"baseUrl": "./src/",
|
||||
"paths": {
|
||||
"@components/*": ["components/commonComponents/*"],
|
||||
"@windows/*": ["components/windows/*"],
|
||||
"@components/*": ["components/*"],
|
||||
"@layouts/*": ["layouts/*"],
|
||||
"@css/*": ["css/*"],
|
||||
"@models/*": ["models/*"],
|
||||
"@assets/*": ["assets/*"]
|
||||
},
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react"
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.astro"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue