Compare commits
No commits in common. "main" and "astro" have entirely different histories.
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"plugins": ["prettier-plugin-astro"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.astro",
|
||||
"options": {
|
||||
"parser": "astro"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"singleQuote": false,
|
||||
"jsxSingleQuote": false,
|
||||
"semi": true,
|
||||
"arrowParens": "avoid",
|
||||
"trailingComma": "none"
|
||||
}
|
4
.vscode/settings.json
vendored
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
}
|
5
LICENSE
|
@ -1,6 +1,3 @@
|
|||
Copyright (c) 2024 nin0dev (https://github.com/nin0-dev, https://codeberg.org/nin0dev, https://git.nin0.dev/nin0, https://nin0.dev)
|
||||
The Rust Bad License
|
||||
|
||||
If you fork this and include The Rust Programming Language files in any way, shape, or form; you will be liable for damages ranging from 10-20 thousand dollars per file.
|
||||
Otherwise, usage and reproduction is prohibited.
|
||||
Usage and reproduction is prohibited.
|
||||
All rights reserved.
|
|
@ -1,3 +1,11 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
|
||||
export default defineConfig({});
|
||||
import node from "@astrojs/node";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
output: "server",
|
||||
adapter: node({
|
||||
mode: "standalone"
|
||||
})
|
||||
});
|
4
info/contacts/discord.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
platform: "Discord"
|
||||
name: "nin0.dev"
|
||||
---
|
6
info/contacts/email.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
platform: "Email"
|
||||
name: "support@nin0.dev"
|
||||
url: "mailto:support@nin0.dev"
|
||||
note: "Replies may be slow"
|
||||
---
|
6
info/contacts/telegram.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
platform: "Telegram"
|
||||
url: "https://nin0dev.t.me"
|
||||
name: "@nin0dev"
|
||||
note: "Use for quick replies"
|
||||
---
|
37
package.json
|
@ -1,21 +1,18 @@
|
|||
{
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^5.7.4",
|
||||
"color.js": "^1.2.0",
|
||||
"sharp": "^0.34.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-astro": "^0.14.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.12.3+sha512.467df2c586056165580ad6dfb54ceaad94c5a30f80893ebdec5a44c5aa73c205ae4a5bb9d5ed6bb84ea7c249ece786642bbb49d06a307df218d03da41c317417"
|
||||
}
|
||||
"name": "",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"7.css": "^0.16.0",
|
||||
"@astrojs/node": "^8.3.2",
|
||||
"astro": "^4.12.2",
|
||||
"lanyard-wrapper": "^2.0.1"
|
||||
}
|
||||
}
|
3886
pnpm-lock.yaml
generated
|
@ -1,4 +0,0 @@
|
|||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- sharp
|
||||
- workerd
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"subject": "acct:personal@nin0.dev",
|
||||
"links": [
|
||||
{
|
||||
"rel": "http://openid.net/specs/connect/1.0/issuer",
|
||||
"href": "https://auth.nin0.dev"
|
||||
}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 283 KiB |
|
@ -1,93 +0,0 @@
|
|||
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.
|
BIN
public/logo.png
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 27 KiB |
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 20.0234 14.1406">
|
||||
<g>
|
||||
<path d="M9.80469 7.65625C10.0312 7.65625 10.2344 7.56641 10.4805 7.34766L18.3477 0.339844C17.9336 0.113281 17.4609 0.0078125 16.8555 0.0078125L2.75781 0.0078125C2.15625 0.0078125 1.68359 0.113281 1.26172 0.339844L9.13281 7.34766C9.37891 7.56641 9.58594 7.65625 9.80469 7.65625ZM0.1875 12.5703L6.01953 6.74609L0.195312 1.55469C0.0859375 1.82031 0 2.23438 0 2.75L0 11.3984C0 11.8711 0.0703125 12.2461 0.1875 12.5703ZM2.62891 14.1406L16.9883 14.1406C17.5547 14.1406 18.0273 14.0195 18.3789 13.8281L12.375 7.82422L11.4336 8.66406C10.8984 9.14062 10.3984 9.35156 9.80469 9.35156C9.21875 9.35156 8.71875 9.14062 8.18359 8.66406L7.24219 7.82422L1.23828 13.8281C1.58984 14.0195 2.05859 14.1406 2.62891 14.1406ZM19.4297 12.5703C19.543 12.2461 19.6172 11.8711 19.6172 11.3984L19.6172 2.75C19.6172 2.23438 19.5312 1.82031 19.4219 1.55469L13.5938 6.74609Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1 KiB |
|
@ -1,4 +0,0 @@
|
|||
<svg role="img" aria-hidden="true" viewBox="0 0 24 24" stroke-width="2" fill="none">
|
||||
<path d="M21 3h-6m6 0l-9 9m9-9v6" stroke-width="2" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M21 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 348 B |
|
@ -1,5 +0,0 @@
|
|||
<?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>
|
Before Width: | Height: | Size: 1.7 KiB |
|
@ -1,9 +0,0 @@
|
|||
<svg viewBox="0 0 212 212" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(6,6)">
|
||||
<path d="M58 168 v-98 a50 50 0 0 1 50-50 h20" stroke="currentColor" stroke-width="25" fill="none" />
|
||||
<path d="M58 168 v-30 a50 50 0 0 1 50-50 h20" stroke="currentColor" stroke-width="25" fill="none" />
|
||||
<circle cx="142" cy="20" r="18" stroke="currentColor" stroke-width="15" fill="none" />
|
||||
<circle cx="142" cy="88" r="18" stroke="currentColor" stroke-width="15" fill="none" />
|
||||
<circle cx="58" cy="180" r="18" stroke="currentColor" stroke-width="15" fill="none" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 593 B |
|
@ -1 +0,0 @@
|
|||
<svg viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="currentColor"/></svg>
|
Before Width: | Height: | Size: 965 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M480-147q-14 0-28.5-5T426-168l-69-63q-106-97-191.5-192.5T80-634q0-94 63-157t157-63q53 0 100 22.5t80 61.5q33-39 80-61.5T660-854q94 0 157 63t63 157q0 115-85 211T602-230l-68 62q-11 11-25.5 16t-28.5 5Z"/></svg>
|
Before Width: | Height: | Size: 322 B |
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 18.0508 17.6562">
|
||||
<g>
|
||||
<path d="M10.0156 17.6562C10.6836 17.6562 11.1406 17.1562 11.4297 16.4023L16.6484 2.74609C16.7773 2.41797 16.8438 2.11719 16.8438 1.86328C16.8438 1.22266 16.4336 0.816406 15.793 0.816406C15.5352 0.816406 15.2344 0.882812 14.9102 1.00781L1.21875 6.24609C0.511719 6.51172 0 6.96875 0 7.64062C0 8.43359 0.574219 8.76953 1.37109 9.01562L5.125 10.1719C5.75 10.3672 6.13672 10.3633 6.59766 9.94922L15.5117 1.79297C15.6328 1.68359 15.7852 1.69531 15.875 1.78516C15.9609 1.875 15.9766 2.02734 15.8633 2.14844L7.72656 11.0547C7.33594 11.4805 7.30078 11.9297 7.48438 12.5312L8.62891 16.2422C8.87891 17.0586 9.21875 17.6562 10.0156 17.6562Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 851 B |
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 12.9879 12.5923">
|
||||
<g>
|
||||
<path d="M0.250141 12.3415C0.589669 12.6732 1.16232 12.669 1.4856 12.3457L6.29154 7.53973L11.0947 12.3443C11.4236 12.6732 11.9956 12.6782 12.3287 12.3401C12.6619 12.0005 12.6633 11.4413 12.3344 11.111L7.53122 6.30005L12.3344 1.49692C12.6633 1.16802 12.6683 0.602398 12.3287 0.269275C11.9892-0.0702532 11.4236-0.0716594 11.0947 0.263651L6.29154 5.06678L1.4856 0.262244C1.16232-0.0660346 0.583263-0.0780657 0.250141 0.267869C-0.0815755 0.607398-0.0773569 1.17224 0.245922 1.49552L5.05187 6.30005L0.245922 11.1138C-0.0773569 11.4357-0.0879818 12.0083 0.250141 12.3415Z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 776 B |
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
import GroupBox from "@components/GroupBox.astro";
|
||||
---
|
||||
|
||||
<style is:global>
|
||||
.ua {
|
||||
font-family: monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
<ass-browser>
|
||||
<GroupBox>
|
||||
<p>
|
||||
<h2>YOUR BROWSER IS ASS</h2>
|
||||
<span>
|
||||
Your browser, <span class="ua"></span> does not support the full
|
||||
set of web features which means that nin0.dev may not work as well
|
||||
as it would on a non-ass browser.
|
||||
<br />
|
||||
<br />
|
||||
Consider switching to a browser that is not ass such as <a
|
||||
href="https://www.apple.com/ca/safari/">Safari</a
|
||||
>, <a href="https://brave.com/">Brave</a>, or if you really have
|
||||
no option, <a href="https://www.google.com/intl/en_ca/chrome/"
|
||||
>Chrome</a
|
||||
>. (these are personal preferences and I am not paid to say
|
||||
this)
|
||||
</span>
|
||||
</p>
|
||||
</GroupBox>
|
||||
</ass-browser>
|
|
@ -1,55 +0,0 @@
|
|||
---
|
||||
import { Image } from "astro:assets";
|
||||
---
|
||||
|
||||
<div class="avatar-wrapper">
|
||||
<Image
|
||||
alt="My profile picture"
|
||||
src={"/logo.png"}
|
||||
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>
|
|
@ -1,107 +1,9 @@
|
|||
---
|
||||
const { position, text, url, id, onClick, name } = Astro.props;
|
||||
import ExternalIcon from "@assets/svg/external.svg";
|
||||
const {position, text, url, id} = Astro.props;
|
||||
---
|
||||
|
||||
<style>
|
||||
.button-wrapper {
|
||||
background: linear-gradient(#343343, #2e2e3e, #2e2e3e);
|
||||
transition: 0.54s
|
||||
linear(
|
||||
0,
|
||||
0.0021,
|
||||
0.0081 1.26%,
|
||||
0.0302 2.51%,
|
||||
0.106,
|
||||
0.2088 7.53%,
|
||||
0.5809 15.7%,
|
||||
0.7667 20.72%,
|
||||
0.8391 23.23%,
|
||||
0.9106 26.37%,
|
||||
0.9538 28.88%,
|
||||
0.9926 32.02%,
|
||||
1.0134,
|
||||
1.0269 37.04%,
|
||||
1.0359,
|
||||
1.0384 43.32%,
|
||||
1.0353 47.72%,
|
||||
1.0124 61.53%,
|
||||
1.003 70.32%,
|
||||
0.9988 81.62%,
|
||||
0.9992 99.83%
|
||||
);
|
||||
clip-path: var(--clip-button-outer);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
button,
|
||||
a {
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
button {
|
||||
border: none;
|
||||
background-color: #2b2b3b;
|
||||
clip-path: var(--clip-button-inner);
|
||||
color: var(--text);
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.button-wrapper:has(button:hover:active) {
|
||||
background: #2f2f41;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
button:hover:active {
|
||||
background-color: #2f2f41;
|
||||
}
|
||||
.link-button {
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
.link-button-heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.link-button-heading > span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
line-height: 0;
|
||||
}
|
||||
:global(.link-button-heading svg) {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
button, a {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
{
|
||||
url ? (
|
||||
<a href={url}>
|
||||
<div class="button-wrapper">
|
||||
<button id={id} class="link-button">
|
||||
{name && (
|
||||
<div class="link-button-heading">
|
||||
<span>
|
||||
<slot name="icon" />
|
||||
<b>{name}</b>
|
||||
</span>
|
||||
<ExternalIcon />
|
||||
</div>
|
||||
)}
|
||||
<slot />
|
||||
</button>
|
||||
</div>
|
||||
</a>
|
||||
) : (
|
||||
<div class="button-wrapper">
|
||||
<button id={id}>
|
||||
<slot />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{url ? <a href={url}><button id={id}>{text}</button></a> : <button id={id}>{text}</button>}
|
10
src/components/ButtonCollection.astro
Normal file
|
@ -0,0 +1,10 @@
|
|||
<style>
|
||||
div {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: 1fr;
|
||||
}
|
||||
</style>
|
||||
<div class="button-collection">
|
||||
<slot />
|
||||
</div>
|
|
@ -1,21 +0,0 @@
|
|||
---
|
||||
const { ...attrs } = Astro.props;
|
||||
---
|
||||
|
||||
<a {...attrs}>
|
||||
<slot />
|
||||
</a>
|
||||
<style>
|
||||
a {
|
||||
clip-path: var(--clip-chip);
|
||||
background-color: rgba(var(--lavender-rgb), 0.2);
|
||||
color: rgba(var(--lavender-rgb), 0.75);
|
||||
font-weight: 600;
|
||||
padding: 2px 7px;
|
||||
margin-right: 10px;
|
||||
gap: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
8
src/components/Contacts.astro
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
import Social from "./Social.astro"
|
||||
|
||||
const contacts = await Astro.glob("../../info/contacts/*.md")
|
||||
---
|
||||
<ul>
|
||||
{contacts.map(contact => <Social platform={contact.frontmatter.platform} url={contact.frontmatter.url} name={contact.frontmatter.name} note={contact.frontmatter.note}/>)}
|
||||
</ul>
|
23
src/components/FearOfTechnology.astro
Normal file
|
@ -0,0 +1,23 @@
|
|||
<style>
|
||||
div {
|
||||
color: black;
|
||||
background-color: lightcoral;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
a {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
|
||||
<noscript>
|
||||
<div>
|
||||
<p>
|
||||
<h4>ENABLE JAVASCRIPT OR I WILL KILL YOU</h4>
|
||||
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!
|
||||
<br/>
|
||||
Rest assured, there's no external tracking. You can check this site's source code on the <a href="https://git.nin0.dev/nin0/website">nin0/website</a> repository on my Forgejo.
|
||||
</p>
|
||||
</div>
|
||||
</noscript>
|
|
@ -1,20 +1,16 @@
|
|||
---
|
||||
const {name} = Astro.props
|
||||
---
|
||||
|
||||
<style>
|
||||
.group-box-wrapper {
|
||||
background: linear-gradient(#343343, #2e2e3e, #2e2e3e);
|
||||
clip-path: var(--clip-group-box-outer);
|
||||
}
|
||||
.group-box {
|
||||
padding: 15px;
|
||||
clip-path: var(--clip-group-box-inner);
|
||||
background-color: #2b2b3b;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
fieldset {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0px;
|
||||
padding: 0px 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="group-box-wrapper">
|
||||
<div class="group-box">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<fieldset>
|
||||
<legend>{name}</legend>
|
||||
<slot />
|
||||
</fieldset>
|
31
src/components/MainWindow.astro
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
import Contacts from "../components/Contacts.astro";
|
||||
import GroupBox from "../components/GroupBox.astro";
|
||||
import Me from "../components/Me.astro";
|
||||
import Presence from "../components/Presence.astro";
|
||||
import Window from "../components/Window.astro";
|
||||
import Button from "./Button.astro";
|
||||
import ButtonCollection from "./ButtonCollection.astro";
|
||||
import Spacer from "./Spacer.astro";
|
||||
---
|
||||
<Window title="Home" maxWidth="600px">
|
||||
<Me />
|
||||
<Presence />
|
||||
<GroupBox name="About me">
|
||||
<p>
|
||||
I'm a Canadian self-taught software developer that makes things some people find useful in Python, HTML, JavaScript, and Kotlin.
|
||||
<br/>
|
||||
I occasionally code with Java or C#, and am currently learning Rust.
|
||||
</p>
|
||||
</GroupBox>
|
||||
<GroupBox name="Reach out">
|
||||
<Contacts />
|
||||
</GroupBox>
|
||||
<ButtonCollection>
|
||||
<Button position="left" text="Projects (soon)" url="/projects"/>
|
||||
<Spacer />
|
||||
<Button position="left" text="Code (soon)" url="/code"/>
|
||||
<Spacer />
|
||||
<Button position="left" text="Blog (ETA: 100 years)" url="/code"/>
|
||||
</ButtonCollection>
|
||||
</Window>
|
45
src/components/Me.astro
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
import {fetchUserData, type DiscordStatus} from "lanyard-wrapper";
|
||||
|
||||
const rawUserData = await fetchUserData("886685857560539176");
|
||||
const statusColor = () => {
|
||||
switch (rawUserData.discord_status) {
|
||||
case "online":
|
||||
return "#23a55a";
|
||||
case "idle":
|
||||
return "#f0b232";
|
||||
case "dnd":
|
||||
return "#f23f43";
|
||||
case "offline":
|
||||
return "#80848e";
|
||||
default:
|
||||
return "#80848e";
|
||||
}
|
||||
};
|
||||
---
|
||||
|
||||
<style define:vars={{statusColor: statusColor()}}>
|
||||
div {
|
||||
display: flex;
|
||||
}
|
||||
h3 {
|
||||
font-weight: 400;
|
||||
margin-left: 20px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
img {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 10px black;
|
||||
border-color: var(--statusColor);
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<img src="logo.png" alt="the nin0dev logo">
|
||||
<h3>nin0dev <span style="font-size: 0.4em;">(he/him)</span></h3>
|
||||
</div>
|
|
@ -1,19 +0,0 @@
|
|||
---
|
||||
import GroupBox from "@components/GroupBox.astro";
|
||||
---
|
||||
|
||||
<noscript>
|
||||
<GroupBox>
|
||||
<p>
|
||||
<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!
|
||||
<br />
|
||||
Rest assured, there's no external tracking. You can check this site's
|
||||
source code on the <a href="https://git.nin0.dev/nin0/website"
|
||||
>nin0/website</a
|
||||
> repository on my Forgejo.
|
||||
</p>
|
||||
</GroupBox>
|
||||
</noscript>
|
55
src/components/Presence.astro
Normal file
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import {fetchUserData} from "lanyard-wrapper";
|
||||
|
||||
const rawUserData = await fetchUserData("886685857560539176");
|
||||
const activity = (() => {
|
||||
let finalActivity = {
|
||||
type: undefined,
|
||||
name: undefined,
|
||||
artist: undefined
|
||||
};
|
||||
rawUserData.activities.some((activity => {
|
||||
if(activity.type === 0) {
|
||||
finalActivity = {
|
||||
type: "playing",
|
||||
name: activity.name,
|
||||
artist: undefined
|
||||
};
|
||||
return;
|
||||
}
|
||||
}));
|
||||
if(finalActivity.type !== undefined) return finalActivity;
|
||||
if(rawUserData.listening_to_spotify) {
|
||||
finalActivity = {
|
||||
type: "listening",
|
||||
name: rawUserData.spotify.song,
|
||||
artist: rawUserData.spotify.artist
|
||||
};
|
||||
};
|
||||
return finalActivity;
|
||||
})();
|
||||
---
|
||||
|
||||
<style>
|
||||
div {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
}
|
||||
p {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-left: 10px;
|
||||
}
|
||||
img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
{
|
||||
activity.type !== undefined &&
|
||||
<div>
|
||||
<img src={activity.type === "playing" ? "/game.ico" : "/music.ico"}>
|
||||
<p>{`${activity.type === "playing" ? "Playing" : "Listening to"} ${activity.name}${activity.type === "listening" ? ` - ${activity.artist}` : ""}`}</p>
|
||||
</div>
|
||||
}
|
16
src/components/Social.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
const {platform, name, url, note} = Astro.props;
|
||||
---
|
||||
|
||||
<style>
|
||||
bl {
|
||||
font-weight: 500;
|
||||
}
|
||||
it {
|
||||
font-style: italic;
|
||||
}
|
||||
</style>
|
||||
|
||||
<li>
|
||||
<bl>{platform}</bl>: {url ? <a href={url}>{name}</a> : name}{note && <it> ({note})</it>}
|
||||
</li>
|
1
src/components/Spacer.astro
Normal file
|
@ -0,0 +1 @@
|
|||
<span />
|
|
@ -1,96 +1,35 @@
|
|||
---
|
||||
const { title, showClose, maxWidth, hideByDefault, customClass, offset } =
|
||||
Astro.props;
|
||||
|
||||
const randomID = customClass || Math.random().toString().replace(".", "");
|
||||
|
||||
import XmarkIcon from "@assets/svg/xmark.svg";
|
||||
const {title, showClose, maxWidth} = Astro.props
|
||||
---
|
||||
|
||||
<style define:vars={{ maxWidth, offset }}>
|
||||
.window {
|
||||
width: var(--maxWidth);
|
||||
top: var(--offset);
|
||||
left: var(--offset);
|
||||
background: var(--surface1);
|
||||
clip-path: var(--clip-window-outer);
|
||||
position: absolute;
|
||||
}
|
||||
.window-inner {
|
||||
background-color: #20202c;
|
||||
clip-path: var(--clip-window-inner);
|
||||
}
|
||||
.title-bar {
|
||||
background-color: var(--crust);
|
||||
user-select: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
height: 42px;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.title-bar span {
|
||||
align-self: center;
|
||||
}
|
||||
svg {
|
||||
fill: var(--text);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 24px;
|
||||
}
|
||||
#close {
|
||||
background: none;
|
||||
color: inherit;
|
||||
border: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 50px;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
outline: inherit;
|
||||
}
|
||||
.window-body {
|
||||
gap: 1rem;
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
@media (pointer: coarse) {
|
||||
.window {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
margin: 5%;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
<style define:vars={{ maxWidth }}>
|
||||
.window {
|
||||
max-width: var(--maxWidth);
|
||||
position: absolute;
|
||||
}
|
||||
.window-body {
|
||||
padding: 20px !important;
|
||||
}
|
||||
@media (pointer:coarse) {
|
||||
.window {
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div
|
||||
class="background"
|
||||
id={`window-${randomID}`}
|
||||
class="window"
|
||||
style={hideByDefault && "display: none;"}
|
||||
>
|
||||
<div class="window" style="max-width: 100%">
|
||||
<div class="window-inner">
|
||||
<div class="title-bar">
|
||||
<span>{title}</span>
|
||||
{
|
||||
showClose && (
|
||||
<button
|
||||
id="close"
|
||||
aria-label="Close"
|
||||
onclick={`document.querySelector("#window-${randomID}").style.display = "none";`}
|
||||
>
|
||||
<XmarkIcon width="12" height="12" />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div class="window-body">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="background" class="window">
|
||||
<div class="window glass active" style="max-width: 100%">
|
||||
<div class="title-bar">
|
||||
<div class="title-bar-text">{title}</div>
|
||||
{
|
||||
showClose &&
|
||||
<div class="title-bar-controls">
|
||||
<button aria-label="Close"></button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="window-body has-space">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
import Button from "@components/Button.astro";
|
||||
import Window from "@components/Window.astro";
|
||||
|
||||
import ForgejoIcon from "@assets/svg/forgejo.svg";
|
||||
import GithubIcon from "@assets/svg/github.svg";
|
||||
---
|
||||
|
||||
<Window
|
||||
title="Code"
|
||||
maxWidth="400px"
|
||||
showClose={true}
|
||||
hideByDefault={true}
|
||||
customClass="code-window"
|
||||
offset="55px"
|
||||
>
|
||||
As mentioned in the main page, I write code. Some used by a lot, some not as
|
||||
much. You can find it below.
|
||||
<Button name="GitHub" url="https://gh.nin0.dev/">
|
||||
<GithubIcon slot="icon" />
|
||||
Here, you'll find most of my external contributions. I rarely start personal
|
||||
projects on GitHub.
|
||||
</Button>
|
||||
<Button name="Forgejo" url="https://git.nin0.dev/nin0">
|
||||
<ForgejoIcon slot="icon" />
|
||||
Some more personal/niche projects can be found on my own instance (git.nin0.dev),
|
||||
which will probably be down.
|
||||
</Button>
|
||||
</Window>
|
|
@ -1,22 +0,0 @@
|
|||
---
|
||||
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 />
|
||||
support@nin0.dev
|
||||
</Chip>
|
||||
<Chip href="https://nin0dev.t.me">
|
||||
<Plane />
|
||||
@nin0dev
|
||||
</Chip>
|
||||
</div>
|
||||
<style>
|
||||
div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
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,49 +0,0 @@
|
|||
---
|
||||
import Contacts from "./Contacts.astro";
|
||||
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}>
|
||||
<InfoStack />
|
||||
|
||||
<Button position="left" id="show-code-window">My code</Button>
|
||||
|
||||
<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>
|
||||
<h3>Reach out</h3>
|
||||
<Contacts />
|
||||
</GroupBox>
|
||||
</Window>
|
|
@ -1,254 +0,0 @@
|
|||
---
|
||||
import LastFMIcon from "@assets/svg/fm.svg";
|
||||
import HeartIcon from "@assets/svg/heart.svg";
|
||||
---
|
||||
|
||||
<style is:global>
|
||||
ass-browser {
|
||||
display: none;
|
||||
}
|
||||
.ass-browser {
|
||||
ass-browser {
|
||||
display: block !important;
|
||||
}
|
||||
.spotify-card {
|
||||
background-color: #2b2b3b !important;
|
||||
}
|
||||
|
||||
.spotify-card-wrapper {
|
||||
background-color: #2b2b3b !important;
|
||||
}
|
||||
|
||||
.spotify-card-background {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="spotify-card-wrapper-shadow">
|
||||
<h3 style="margin-bottom: 10px;">Listening to</h3>
|
||||
<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>
|
||||
<HeartIcon id="loved-icon" />
|
||||
</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>
|
||||
h4 {
|
||||
color: var(--subtext0);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.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);
|
||||
|
||||
#loved-icon {
|
||||
display: var(--loved);
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
bottom: -6px;
|
||||
right: -5px;
|
||||
fill: var(--red);
|
||||
stroke: #641c2c;
|
||||
stroke-width: 50px;
|
||||
filter: drop-shadow(0 0 19px #0000006d);
|
||||
}
|
||||
}
|
||||
|
||||
.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, LASTFM_USER } from "utils/constants";
|
||||
import {
|
||||
type FMResponse,
|
||||
type FMRawTrack,
|
||||
type FMTrack,
|
||||
transformRawTrack
|
||||
} from "types/lastfm";
|
||||
const spotifyCardWrapper: HTMLElement = document.querySelector(
|
||||
".spotify-card-wrapper-shadow"
|
||||
);
|
||||
|
||||
async function getLastFmTracks() {
|
||||
const params = new URLSearchParams({
|
||||
method: "user.getrecenttracks",
|
||||
api_key: LASTFM_KEY,
|
||||
user: LASTFM_USER,
|
||||
limit: "50",
|
||||
format: "json",
|
||||
extended: "true"
|
||||
});
|
||||
|
||||
const response: FMResponse = await (
|
||||
await fetch(`${LASTFM_API}${params}`)
|
||||
).json();
|
||||
|
||||
if (
|
||||
response.recenttracks.track.some(t => {
|
||||
try {
|
||||
return t["@attr"].nowplaying === "true";
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
) {
|
||||
const rawTrack: FMRawTrack = response.recenttracks.track[0];
|
||||
const track: FMTrack = transformRawTrack(rawTrack);
|
||||
|
||||
mergeStyles(
|
||||
{
|
||||
display: "block",
|
||||
"--track": `'${track.name.replaceAll("'", "\\'")}'`,
|
||||
"--artist": `'${track.artist.replaceAll("'", "\\'")}'`,
|
||||
"--album": `'${track.album.replaceAll("'", "\\'")}'`,
|
||||
"--album-art": `url("${track.albumArt}")`,
|
||||
"--album-color": await average(track.albumArt, {
|
||||
format: "hex"
|
||||
}),
|
||||
"--loved": track.loved ? "block" : "none"
|
||||
},
|
||||
spotifyCardWrapper.style
|
||||
);
|
||||
|
||||
(document.querySelector(".lastfm-link") as HTMLLinkElement).href =
|
||||
track.url;
|
||||
|
||||
if (
|
||||
new URLSearchParams(window.location.search).has("iamanidiot") &&
|
||||
(track.artist.includes("Taylor Swift") ||
|
||||
track.name.includes("Taylor Swift"))
|
||||
) {
|
||||
spotifyCardWrapper.style = "";
|
||||
}
|
||||
} else {
|
||||
spotifyCardWrapper.style = "";
|
||||
}
|
||||
}
|
||||
|
||||
getLastFmTracks();
|
||||
setInterval(getLastFmTracks, 10000);
|
||||
</script>
|
|
@ -1,81 +0,0 @@
|
|||
/* https://github.com/catppuccin/palette/blob/main/docs/css.md */
|
||||
:root {
|
||||
--rosewater: #f5e0dc;
|
||||
--rosewater-rgb: 245, 224, 220;
|
||||
--rosewater-hsl: 9.6, 55.556%, 91.176%;
|
||||
--flamingo: #f2cdcd;
|
||||
--flamingo-rgb: 242, 205, 205;
|
||||
--flamingo-hsl: 0, 58.73%, 87.647%;
|
||||
--pink: #f5c2e7;
|
||||
--pink-rgb: 245, 194, 231;
|
||||
--pink-hsl: 316.471, 71.831%, 86.078%;
|
||||
--mauve: #cba6f7;
|
||||
--mauve-rgb: 203, 166, 247;
|
||||
--mauve-hsl: 267.407, 83.505%, 80.98%;
|
||||
--red: #f38ba8;
|
||||
--red-rgb: 243, 139, 168;
|
||||
--red-hsl: 343.269, 81.25%, 74.902%;
|
||||
--maroon: #eba0ac;
|
||||
--maroon-rgb: 235, 160, 172;
|
||||
--maroon-hsl: 350.4, 65.217%, 77.451%;
|
||||
--peach: #fab387;
|
||||
--peach-rgb: 250, 179, 135;
|
||||
--peach-hsl: 22.957, 92%, 75.49%;
|
||||
--yellow: #f9e2af;
|
||||
--yellow-rgb: 249, 226, 175;
|
||||
--yellow-hsl: 41.351, 86.047%, 83.137%;
|
||||
--green: #a6e3a1;
|
||||
--green-rgb: 166, 227, 161;
|
||||
--green-hsl: 115.455, 54.098%, 76.078%;
|
||||
--teal: #94e2d5;
|
||||
--teal-rgb: 148, 226, 213;
|
||||
--teal-hsl: 170, 57.353%, 73.333%;
|
||||
--sky: #89dceb;
|
||||
--sky-rgb: 137, 220, 235;
|
||||
--sky-hsl: 189.184, 71.014%, 72.941%;
|
||||
--sapphire: #74c7ec;
|
||||
--sapphire-rgb: 116, 199, 236;
|
||||
--sapphire-hsl: 198.5, 75.949%, 69.02%;
|
||||
--blue: #89b4fa;
|
||||
--blue-rgb: 137, 180, 250;
|
||||
--blue-hsl: 217.168, 91.87%, 75.882%;
|
||||
--lavender: #b4befe;
|
||||
--lavender-rgb: 180, 190, 254;
|
||||
--lavender-hsl: 231.892, 97.368%, 85.098%;
|
||||
--text: #cdd6f4;
|
||||
--text-rgb: 205, 214, 244;
|
||||
--text-hsl: 226.154, 63.934%, 88.039%;
|
||||
--subtext1: #bac2de;
|
||||
--subtext1-rgb: 186, 194, 222;
|
||||
--subtext1-hsl: 226.667, 35.294%, 80%;
|
||||
--subtext0: #a6adc8;
|
||||
--subtext0-rgb: 166, 173, 200;
|
||||
--subtext0-hsl: 227.647, 23.611%, 71.765%;
|
||||
--overlay2: #9399b2;
|
||||
--overlay2-rgb: 147, 153, 178;
|
||||
--overlay2-hsl: 228.387, 16.757%, 63.725%;
|
||||
--overlay1: #7f849c;
|
||||
--overlay1-rgb: 127, 132, 156;
|
||||
--overlay1-hsl: 229.655, 12.775%, 55.49%;
|
||||
--overlay0: #6c7086;
|
||||
--overlay0-rgb: 108, 112, 134;
|
||||
--overlay0-hsl: 230.769, 10.744%, 47.451%;
|
||||
--surface2: #585b70;
|
||||
--surface2-rgb: 88, 91, 112;
|
||||
--surface2-hsl: 232.5, 12%, 39.216%;
|
||||
--surface1: #45475a;
|
||||
--surface1-rgb: 69 71 90;
|
||||
--surface1-hsl: 234.286, 13.208%, 31.176%;
|
||||
--surface0: #313244;
|
||||
--surface0-rgb: 49 50 68;
|
||||
--surface0-hsl: 236.842, 16.239%, 22.941%;
|
||||
--base: #1e1e2e;
|
||||
--base-rgb: 30, 30, 46;
|
||||
--base-hsl: 240, 21.053%, 14.902%;
|
||||
--mantle: #181825;
|
||||
--mantle-rgb: 24, 24, 37;
|
||||
--mantle-hsl: 240, 21.311%, 11.961%;
|
||||
--crust: #11111b;
|
||||
--crust-rgb: 17, 17, 27;
|
||||
--crust-hsl: 240, 22.727%, 8.627%;
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
@import "@css/catppuccin.css";
|
||||
@import "@css/clip-paths.css";
|
||||
@import "@css/inter.css";
|
||||
|
||||
:root {
|
||||
background: url("/background.png");
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
* {
|
||||
font-family: BlinkMacSystemFont, "Inter", sans-serif;
|
||||
}
|
||||
body {
|
||||
font-size: 0.95rem;
|
||||
font-weight: 300;
|
||||
color: var(--text);
|
||||
padding: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
a {
|
||||
color: #89b4fa;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
ul {
|
||||
margin: 0;
|
||||
}
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
sub {
|
||||
color: var(--overlay0);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
@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");
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
.me-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
.img-container {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
clip-path: var(--clip-avatar-outer);
|
||||
img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
clip-path: var(--clip-avatar-inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spotify-card-wrapper {
|
||||
background: var(--album-color);
|
||||
position: relative;
|
||||
clip-path: var(--clip-sp-1);
|
||||
.spotify-card {
|
||||
.secondary-meta {
|
||||
color: #a6adc8;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
padding: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
clip-path: var(--clip-sp-2);
|
||||
}
|
||||
}
|
||||
|
||||
.spotify-card-wrapper::before {
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
clip-path: var(--clip-sp-3);
|
||||
position: absolute;
|
||||
background-image: var(--album-art);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
filter: blur(20px) brightness(0.5);
|
||||
}
|
11
src/css/style.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
body {
|
||||
background-color: #56a0d1;
|
||||
padding: 30px;
|
||||
font-family: sans-serif !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
@media (pointer:coarse) {
|
||||
body {
|
||||
overflow: scroll;
|
||||
}
|
||||
}
|
1
src/env.d.ts
vendored
|
@ -1,2 +1 @@
|
|||
/// <reference types="astro/client" />
|
||||
/// <reference types="astro/astro-jsx" />
|
||||
|
|
56
src/js/drag.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
// wow i love being fake programmer
|
||||
// source: https://jams.hackclub.com/batch/webOS/part-3
|
||||
function dragElement(element) {
|
||||
// Step 2: Set up variables to keep track of the element's position.
|
||||
var initialX = 0;
|
||||
var initialY = 0;
|
||||
var currentX = 0;
|
||||
var currentY = 0;
|
||||
|
||||
// Step 3: Check if there is a special header element associated with the draggable element.
|
||||
if (document.getElementById("title-bar")) {
|
||||
// Step 4: If present, assign the `dragMouseDown` function to the header's `onmousedown` event.
|
||||
// This allows you to drag the window around by its header.
|
||||
document.getElementById("title-bar").onmousedown = startDragging;
|
||||
} else {
|
||||
// Step 5: If not present, assign the function directly to the draggable element's `onmousedown` event.
|
||||
// This allows you to drag the window by holding down anywhere on the window.
|
||||
element.onmousedown = startDragging;
|
||||
}
|
||||
|
||||
// Step 6: Define the `startDragging` function to capture the initial mouse position and set up event listeners.
|
||||
function startDragging(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// Step 7: Get the mouse cursor position at startup.
|
||||
initialX = e.clientX;
|
||||
initialY = e.clientY;
|
||||
// Step 8: Set up event listeners for mouse movement (`elementDrag`) and mouse button release (`closeDragElement`).
|
||||
document.onmouseup = stopDragging;
|
||||
document.onmousemove = dragElement;
|
||||
}
|
||||
|
||||
// Step 9: Define the `elementDrag` function to calculate the new position of the element based on mouse movement.
|
||||
function dragElement(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// Step 10: Calculate the new cursor position.
|
||||
currentX = initialX - e.clientX;
|
||||
currentY = initialY - e.clientY;
|
||||
initialX = e.clientX;
|
||||
initialY = e.clientY;
|
||||
// Step 11: Update the element's new position by modifying its `top` and `left` CSS properties.
|
||||
console.log(element.offsetTop);
|
||||
console.log(currentX);
|
||||
element.style.top = (element.offsetTop - currentY) + "px";
|
||||
element.style.left = (element.offsetLeft - currentX) + "px";
|
||||
}
|
||||
|
||||
// Step 12: Define the `stopDragging` function to stop tracking mouse movement by removing the event listeners.
|
||||
function stopDragging() {
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
console.log(element.offsetTop - currentY);
|
||||
console.log(element.offsetLeft - currentX);
|
||||
}
|
||||
}
|
|
@ -1,35 +1,76 @@
|
|||
---
|
||||
import "@css/global.css";
|
||||
const { tabTitle } = Astro.props;
|
||||
import "7.css/dist/7.css";
|
||||
import "../css/style.css";
|
||||
|
||||
const {tabTitle} = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<meta name="darkreader-lock">
|
||||
<title>{tabTitle}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
<script>
|
||||
import { WindowManager } from "@models/WindowManager.ts";
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document
|
||||
.querySelectorAll(".window")
|
||||
.forEach(window => WindowManager.observe(window));
|
||||
|
||||
const isBrowserAss = () =>
|
||||
["Firefox"].some(ass => navigator.userAgent.includes(ass));
|
||||
if (isBrowserAss()) document.body.classList.add("ass-browser");
|
||||
|
||||
document
|
||||
.querySelectorAll(".ua")
|
||||
// @ts-ignore
|
||||
.forEach(u => (u.innerText = navigator.userAgent));
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{tabTitle}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
<script>
|
||||
function dragElement(element) {
|
||||
// Step 2: Set up variables to keep track of the element's position.
|
||||
var initialX = 0;
|
||||
var initialY = 0;
|
||||
var currentX = 0;
|
||||
var currentY = 0;
|
||||
|
||||
// Step 3: Check if there is a special header element associated with the draggable element.
|
||||
if (document.getElementById("title-bar")) {
|
||||
// Step 4: If present, assign the `dragMouseDown` function to the header's `onmousedown` event.
|
||||
// This allows you to drag the window around by its header.
|
||||
document.getElementById("title-bar").onmousedown = startDragging;
|
||||
} else {
|
||||
// Step 5: If not present, assign the function directly to the draggable element's `onmousedown` event.
|
||||
// This allows you to drag the window by holding down anywhere on the window.
|
||||
element.onmousedown = startDragging;
|
||||
}
|
||||
|
||||
// Step 6: Define the `startDragging` function to capture the initial mouse position and set up event listeners.
|
||||
function startDragging(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// Step 7: Get the mouse cursor position at startup.
|
||||
initialX = e.clientX;
|
||||
initialY = e.clientY;
|
||||
// Step 8: Set up event listeners for mouse movement (`elementDrag`) and mouse button release (`closeDragElement`).
|
||||
document.onmouseup = stopDragging;
|
||||
document.onmousemove = dragElement;
|
||||
}
|
||||
|
||||
// Step 9: Define the `elementDrag` function to calculate the new position of the element based on mouse movement.
|
||||
function dragElement(e) {
|
||||
e = e || window.event;
|
||||
e.preventDefault();
|
||||
// Step 10: Calculate the new cursor position.
|
||||
currentX = initialX - e.clientX;
|
||||
currentY = initialY - e.clientY;
|
||||
initialX = e.clientX;
|
||||
initialY = e.clientY;
|
||||
// Step 11: Update the element's new position by modifying its `top` and `left` CSS properties.
|
||||
element.style.top = (element.offsetTop - currentY) + "px";
|
||||
element.style.left = (element.offsetLeft - currentX) + "px";
|
||||
}
|
||||
|
||||
// Step 12: Define the `stopDragging` function to stop tracking mouse movement by removing the event listeners.
|
||||
function stopDragging() {
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
}
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.querySelectorAll(".window").forEach(window => {
|
||||
dragElement(window);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
import type {
|
||||
AnyLanyardPayload,
|
||||
LanyardInitStatePayload,
|
||||
LanyardPayload,
|
||||
LanyardPresence,
|
||||
LanyardPresenceUpdatePayload
|
||||
} from "types/lanyard";
|
||||
|
||||
export class Lanyard {
|
||||
private socket: WebSocket;
|
||||
private readonly userId: string;
|
||||
private readonly updateCallback: (presence: LanyardPresence) => void;
|
||||
private heartbeatInterval?: number;
|
||||
|
||||
constructor(
|
||||
userId: string,
|
||||
updateCallback: (presence: LanyardPresence) => void
|
||||
) {
|
||||
this.userId = userId;
|
||||
this.updateCallback = updateCallback;
|
||||
this.socket = new WebSocket("wss://api.lanyard.rest/socket");
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
private send(data: AnyLanyardPayload) {
|
||||
this.socket.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
private initialize() {
|
||||
this.socket.onmessage = event => {
|
||||
const payload: LanyardPayload = JSON.parse(event.data);
|
||||
switch (payload.op) {
|
||||
case 1: {
|
||||
// Initialize
|
||||
this.send({
|
||||
op: 2,
|
||||
d: {
|
||||
subscribe_to_ids: [this.userId]
|
||||
}
|
||||
});
|
||||
|
||||
this.send({ op: 3 });
|
||||
|
||||
const interval =
|
||||
"heartbeat_interval" in payload.d
|
||||
? payload.d.heartbeat_interval
|
||||
: 30000;
|
||||
|
||||
this.heartbeatInterval = window.setInterval(() => {
|
||||
this.send({ op: 3 });
|
||||
}, interval);
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
const typedPayload = payload as
|
||||
| LanyardInitStatePayload
|
||||
| LanyardPresenceUpdatePayload;
|
||||
|
||||
const presence =
|
||||
typedPayload.t === "INIT_STATE"
|
||||
? typedPayload.d[this.userId]
|
||||
: typedPayload.d;
|
||||
|
||||
this.updateCallback(presence);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public close() {
|
||||
if (this.heartbeatInterval) {
|
||||
clearInterval(this.heartbeatInterval);
|
||||
}
|
||||
this.socket.close();
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
export class WindowManager {
|
||||
private static _topmost: HTMLElement | null;
|
||||
|
||||
static get topmost() {
|
||||
return this._topmost;
|
||||
}
|
||||
|
||||
static set topmost(e) {
|
||||
if (this._topmost) {
|
||||
this._topmost.style.zIndex = "0";
|
||||
}
|
||||
this._topmost = e;
|
||||
this._topmost.style.display = "block";
|
||||
this._topmost.style.zIndex = "1";
|
||||
}
|
||||
|
||||
static observe(element) {
|
||||
element.addEventListener("mousedown", () => {
|
||||
this.topmost = element;
|
||||
});
|
||||
|
||||
const titleBar = element.querySelector(".title-bar");
|
||||
|
||||
titleBar.addEventListener("mousedown", startDrag);
|
||||
titleBar.addEventListener("touchstart", startDrag, { passive: false });
|
||||
|
||||
function startDrag(e) {
|
||||
if (e.target !== titleBar) return;
|
||||
|
||||
let isTouch = e.type.startsWith("touch");
|
||||
let startX = isTouch ? e.touches[0].clientX : e.clientX;
|
||||
let startY = isTouch ? e.touches[0].clientY : e.clientY;
|
||||
|
||||
let offsetX = startX - element.offsetLeft;
|
||||
let offsetY = startY - element.offsetTop;
|
||||
|
||||
function onMove(e) {
|
||||
let clientX = isTouch ? e.touches[0].clientX : e.clientX;
|
||||
let clientY = isTouch ? e.touches[0].clientY : e.clientY;
|
||||
|
||||
let newX = clientX - offsetX;
|
||||
let newY = clientY - offsetY;
|
||||
|
||||
element.style.top = `${newY}px`;
|
||||
element.style.left = `${newX}px`;
|
||||
}
|
||||
|
||||
function stopMove() {
|
||||
let maxX = window.innerWidth - element.offsetWidth;
|
||||
let maxY = window.innerHeight - element.offsetHeight;
|
||||
|
||||
let finalX = Math.min(Math.max(element.offsetLeft, 0), maxX);
|
||||
let finalY = Math.min(Math.max(element.offsetTop, 0), maxY);
|
||||
|
||||
element.style.top = `${finalY}px`;
|
||||
element.style.left = `${finalX}px`;
|
||||
|
||||
document.removeEventListener("mousemove", onMove);
|
||||
document.removeEventListener("mouseup", stopMove);
|
||||
document.removeEventListener("touchmove", onMove);
|
||||
document.removeEventListener("touchend", stopMove);
|
||||
}
|
||||
|
||||
document.addEventListener("mousemove", onMove);
|
||||
document.addEventListener("mouseup", stopMove);
|
||||
document.addEventListener("touchmove", onMove, { passive: false });
|
||||
document.addEventListener("touchend", stopMove);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,10 @@
|
|||
---
|
||||
import Noscript from "@components/Noscript.astro";
|
||||
import MainWindow from "@components/windows/main/MainWindow.astro";
|
||||
import FearOfTechnology from "../components/FearOfTechnology.astro";
|
||||
import MainWindow from "../components/MainWindow.astro";
|
||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||
import CodeWindow from "@components/windows/code/CodeWindow.astro";
|
||||
import AssBrowser from "@components/AssBrowser.astro";
|
||||
---
|
||||
|
||||
<BaseLayout tabTitle="nin0.dev">
|
||||
<Noscript />
|
||||
<AssBrowser />
|
||||
<BaseLayout tabTitle="Home - nin0dev">
|
||||
<FearOfTechnology />
|
||||
<MainWindow />
|
||||
<CodeWindow />
|
||||
</BaseLayout>
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
export interface LanyardPresence {
|
||||
listening_to_spotify: boolean;
|
||||
spotify: {
|
||||
track_id: string;
|
||||
timestamps: {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
song: string;
|
||||
artist: string;
|
||||
album_art_url: string;
|
||||
album: string;
|
||||
};
|
||||
discord_status: string;
|
||||
activities: {
|
||||
type: number;
|
||||
timestamps: {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
state: string;
|
||||
name: string;
|
||||
id: string;
|
||||
details: string;
|
||||
assets: {
|
||||
small_text: string;
|
||||
small_image: string;
|
||||
large_text: string;
|
||||
large_image: string;
|
||||
};
|
||||
application_id: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface AnyLanyardPayload {
|
||||
op: 0 | 1 | 2 | 3;
|
||||
d?: object;
|
||||
}
|
||||
|
||||
export interface LanyardHelloPayload {
|
||||
op: 1;
|
||||
d: {
|
||||
heartbeat_interval: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LanyardInitStatePayload {
|
||||
op: 0;
|
||||
t: "INIT_STATE";
|
||||
d: {
|
||||
[user_id: string]: LanyardPresence;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LanyardPresenceUpdatePayload {
|
||||
op: 0;
|
||||
t: "PRESENCE_UPDATE";
|
||||
d: LanyardPresence;
|
||||
}
|
||||
|
||||
export type LanyardPayload =
|
||||
| LanyardHelloPayload
|
||||
| LanyardInitStatePayload
|
||||
| LanyardPresenceUpdatePayload
|
||||
| AnyLanyardPayload;
|
|
@ -1,44 +0,0 @@
|
|||
export interface FMRawTrack {
|
||||
name: string;
|
||||
url: string;
|
||||
album: {
|
||||
"#text": string;
|
||||
};
|
||||
artist: {
|
||||
name: string;
|
||||
};
|
||||
image: Array<{
|
||||
size: string;
|
||||
"#text": string;
|
||||
}>;
|
||||
loved: string;
|
||||
"@attr"?: {
|
||||
nowplaying?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FMTrack {
|
||||
name: string;
|
||||
artist: string;
|
||||
album: string;
|
||||
url: string;
|
||||
albumArt: string;
|
||||
loved: boolean;
|
||||
}
|
||||
|
||||
export interface FMResponse {
|
||||
recenttracks: {
|
||||
track: FMRawTrack[];
|
||||
};
|
||||
}
|
||||
|
||||
export function transformRawTrack(raw: FMRawTrack): FMTrack {
|
||||
return {
|
||||
name: raw.name,
|
||||
artist: raw.artist.name,
|
||||
album: raw.album["#text"],
|
||||
url: raw.url,
|
||||
albumArt: raw.image[1]["#text"],
|
||||
loved: raw.loved === "1"
|
||||
};
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
export const LASTFM_KEY: string = "3c5623fa1abbd11c49f53ca18e992ead";
|
||||
export const LASTFM_API: string = "https://ws.audioscrobbler.com/2.0/?";
|
||||
export const LASTFM_USER: string = "nin0dev";
|
||||
export const LANYARD_ID: string = "886685857560539176";
|
||||
export const LANYARD_API: string = "wss://api.lanyard.rest/socket";
|
|
@ -1,5 +0,0 @@
|
|||
export function mergeStyles(source, target: CSSStyleDeclaration) {
|
||||
for (const prop in source) {
|
||||
target.setProperty(prop, source[prop]);
|
||||
}
|
||||
}
|
|
@ -1,13 +1,3 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./src/",
|
||||
"paths": {
|
||||
"@components/*": ["components/*"],
|
||||
"@layouts/*": ["layouts/*"],
|
||||
"@css/*": ["css/*"],
|
||||
"@models/*": ["models/*"],
|
||||
"@assets/*": ["assets/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
"extends": "astro/tsconfigs/base"
|
||||
}
|
||||
|
|