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 { defineConfig } from 'astro/config';
|
||||||
import node from "@astrojs/node";
|
|
||||||
|
|
||||||
import cloudflare from "@astrojs/cloudflare";
|
export default defineConfig({});
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
15
package.json
15
package.json
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
"name": "",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -10,21 +9,11 @@
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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",
|
"astro": "^5.7.4",
|
||||||
"color.js": "^1.2.0",
|
"color.js": "^1.2.0"
|
||||||
"react": "^19.1.0",
|
|
||||||
"react-dom": "^19.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-astro": "^0.14.1",
|
"prettier-plugin-astro": "^0.14.1"
|
||||||
"wrangler": "^3.114.6"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
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
|
const { ...attrs } = Astro.props
|
||||||
---
|
---
|
||||||
<a {...attrs}>
|
<a {...attrs}>
|
||||||
<slot name="icon" />
|
|
||||||
<slot />
|
<slot />
|
||||||
</a>
|
</a>
|
||||||
<style>
|
<style>
|
|
@ -1,7 +1,3 @@
|
||||||
---
|
|
||||||
const { name } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.group-box-wrapper {
|
.group-box-wrapper {
|
||||||
background: linear-gradient(#343343, #2E2E3E, #2E2E3E);
|
background: linear-gradient(#343343, #2E2E3E, #2E2E3E);
|
||||||
|
@ -19,7 +15,6 @@ const { name } = Astro.props;
|
||||||
|
|
||||||
<div class="group-box-wrapper">
|
<div class="group-box-wrapper">
|
||||||
<div class="group-box">
|
<div class="group-box">
|
||||||
<h3>{name}</h3>
|
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,20 +1,11 @@
|
||||||
<style>
|
---
|
||||||
div {
|
import GroupBox from "@components/GroupBox.astro";
|
||||||
color: black;
|
---
|
||||||
background-color: lightcoral;
|
|
||||||
padding: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<noscript>
|
<noscript>
|
||||||
<div>
|
<GroupBox>
|
||||||
<p>
|
<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
|
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,
|
you have JavaScript disabled. This means that some features (eg. dragging,
|
||||||
submitting forms) will not work. Enable JS to fully enjoy this website!
|
submitting forms) will not work. Enable JS to fully enjoy this website!
|
||||||
|
@ -24,5 +15,5 @@
|
||||||
>nin0/website</a
|
>nin0/website</a
|
||||||
> repository on my Forgejo.
|
> repository on my Forgejo.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</GroupBox>
|
||||||
</noscript>
|
</noscript>
|
|
@ -1,16 +1,16 @@
|
||||||
---
|
---
|
||||||
import Chip from "./Chip.astro";
|
import Chip from "@components/Chip.astro";
|
||||||
import Envelope from "@assets/svg/envelope.svg";
|
import Envelope from "@assets/svg/envelope.svg";
|
||||||
import Plane from "@assets/svg/plane.svg";
|
import Plane from "@assets/svg/plane.svg";
|
||||||
---
|
---
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Chip href="mailto:support@nin0.dev">
|
<Chip href="mailto:support@nin0.dev">
|
||||||
<Envelope slot="icon" />
|
<Envelope />
|
||||||
support@nin0.dev
|
support@nin0.dev
|
||||||
</Chip>
|
</Chip>
|
||||||
<Chip href="https://nin0dev.t.me">
|
<Chip href="https://nin0dev.t.me">
|
||||||
<Plane slot="icon" />
|
<Plane />
|
||||||
@nin0dev
|
@nin0dev
|
||||||
</Chip>
|
</Chip>
|
||||||
</div>
|
</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 Contacts from "./Contacts.astro";
|
||||||
import GroupBox from "@components/GroupBox.astro";
|
import InfoStack from "./InfoStack.astro";
|
||||||
import Me from "./Me";
|
|
||||||
import Window from "@components/Window.astro";
|
import Window from "@components/Window.astro";
|
||||||
import Button from "@components/Button.astro";
|
import Button from "@components/Button.astro";
|
||||||
|
import GroupBox from "@components/GroupBox.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { WindowManager } from "@models/WindowManager";
|
import { WindowManager } from "@models/WindowManager";
|
||||||
|
import { Lanyard } from "@models/Lanyard";
|
||||||
|
import { LANYARD_ID } from "utils/constants";
|
||||||
|
|
||||||
document
|
document
|
||||||
.querySelector("#show-code-window")
|
.querySelector("#show-code-window")
|
||||||
.addEventListener("click", () => WindowManager.topmost = document.getElementById("window-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>
|
</script>
|
||||||
|
|
||||||
<Window title="Home" maxWidth="600px" showClose={false}>
|
<Window title="Home" maxWidth="600px" showClose={false}>
|
||||||
<Me client:only />
|
<InfoStack />
|
||||||
|
|
||||||
<Button position="left" id="show-code-window">My code</Button>
|
<Button position="left" id="show-code-window">My code</Button>
|
||||||
<GroupBox name="About me">
|
|
||||||
|
<GroupBox>
|
||||||
|
<h3>About me</h3>
|
||||||
<p>
|
<p>
|
||||||
I'm a Canadian self-taught software developer.
|
I'm a Canadian self-taught software developer.
|
||||||
I also make things that some people use with varying degrees of usefulness.
|
I also make things that some people use with varying degrees of usefulness.
|
||||||
</p>
|
</p>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
<GroupBox name="Reach out">
|
|
||||||
|
<GroupBox>
|
||||||
|
<h3>Reach out</h3>
|
||||||
<Contacts />
|
<Contacts />
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
</Window>
|
</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/catppuccin.css";
|
||||||
|
@import "@css/clip-paths.css";
|
||||||
|
@import "@css/inter.css";
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: url("/background.png");
|
background: url("/background.png");
|
||||||
|
background-size: cover;
|
||||||
|
height: 100vh;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
* {
|
* {
|
||||||
font-family: BlinkMacSystemFont, Inter, sans-serif;
|
font-family: BlinkMacSystemFont, "Inter", sans-serif;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
background: var(--background);
|
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
|
font-weight: 300;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
@ -17,17 +21,9 @@ body {
|
||||||
a {
|
a {
|
||||||
color: #89b4fa;
|
color: #89b4fa;
|
||||||
}
|
}
|
||||||
@media (pointer: coarse) {
|
|
||||||
body {
|
|
||||||
overflow: scroll;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.emoji {
|
|
||||||
font-family: "Inter", "Noto Emoji Variable";
|
|
||||||
}
|
|
||||||
h1,h2,h3,h4,h5,h6,p,ul {
|
h1,h2,h3,h4,h5,h6,p,ul {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
h4,h5,h6 {
|
h4,h5,h6,sub {
|
||||||
color: var(--overlay0)
|
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;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
clip-path: var(--clip-sp-2);
|
clip-path: var(--clip-sp-2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
---
|
---
|
||||||
import "@css/global.css";
|
import "@css/global.css";
|
||||||
import "@css/clip-paths.css";
|
|
||||||
import "@fontsource/inter";
|
|
||||||
import "@fontsource-variable/noto-emoji";
|
|
||||||
|
|
||||||
const { tabTitle } = Astro.props;
|
const { tabTitle } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
---
|
---
|
||||||
import FearOfTechnology from "@windows/mainWindow/FearOfTechnology.astro";
|
import Noscript from "@components/Noscript.astro";
|
||||||
import MainWindow from "@windows/mainWindow/MainWindow.astro";
|
import MainWindow from "@components/windows/main/MainWindow.astro";
|
||||||
import BaseLayout from "../layouts/BaseLayout.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">
|
<BaseLayout tabTitle="nin0.dev">
|
||||||
<FearOfTechnology />
|
<Noscript />
|
||||||
<MainWindow />
|
<MainWindow />
|
||||||
<CodeWindow />
|
<CodeWindow />
|
||||||
</BaseLayout>
|
</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": {
|
"compilerOptions": {
|
||||||
"baseUrl": "./src/",
|
"baseUrl": "./src/",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@components/*": ["components/commonComponents/*"],
|
"@components/*": ["components/*"],
|
||||||
"@windows/*": ["components/windows/*"],
|
|
||||||
"@layouts/*": ["layouts/*"],
|
"@layouts/*": ["layouts/*"],
|
||||||
"@css/*": ["css/*"],
|
"@css/*": ["css/*"],
|
||||||
"@models/*": ["models/*"],
|
"@models/*": ["models/*"],
|
||||||
"@assets/*": ["assets/*"]
|
"@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