Signed-off-by: darwinx64 <tiramisyuz@proton.me>
This commit is contained in:
darwinx64 2025-04-19 23:45:40 -05:00 committed by darwinx64
parent 8ed804a5e6
commit 98412adab9
No known key found for this signature in database
35 changed files with 1821 additions and 1424 deletions

View file

@ -1,29 +1,80 @@
---
const { position, text, url, id, onClick } = Astro.props;
const { position, text, url, id, onClick, name } = Astro.props;
import ExternalIcon from "@assets/svg/external.svg";
---
<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 {
background-color: #45475a;
border: 1px var(--text) solid;
border-radius: 10px;
border: none;
background-color: #2B2B3B;
clip-path: var(--clip-button-inner);
color: var(--text);
padding: 8px 20px;
padding: 10px 20px;
}
button:hover {
background-color: #585b70;
.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;
}
</style>
{
url ? (
<a href={url}>
<button id={id}>{text}</button>
<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>
) : (
<button id={id}>{text}</button>
<div class="button-wrapper">
<button id={id}><slot /></button>
</div>
)
}

View file

@ -1,11 +0,0 @@
<style>
div {
display: grid;
grid-auto-flow: row;
row-gap: 8px;
margin-top: 15px;
}
</style>
<div class="button-collection">
<slot />
</div>

View file

@ -3,17 +3,23 @@ const { name } = Astro.props;
---
<style>
div {
margin-top: 15px;
margin-bottom: 0px;
padding: 0px 15px;
border-radius: 15px;
border: 1px solid #313244;
background-color: #313244;
.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;
}
</style>
<div>
<h3>{name}</h3>
<slot />
</div>
<div class="group-box-wrapper">
<div class="group-box">
<h3>{name}</h3>
<slot />
</div>
</div>

View file

@ -1 +0,0 @@
<span></span>

View file

@ -3,6 +3,8 @@ const { title, showClose, maxWidth, hideByDefault, customClass, offset } =
Astro.props;
const randomID = customClass || Math.random().toString().replace(".", "");
import XmarkIcon from "@assets/svg/xmark.svg";
---
<style define:vars={{ maxWidth, offset }}>
@ -10,25 +12,30 @@ const randomID = customClass || Math.random().toString().replace(".", "");
width: var(--maxWidth);
top: var(--offset);
left: var(--offset);
border-radius: 17px;
border: 1px solid var(--text);
background: var(--surface1);
clip-path: var(--clip-window-outer);
position: absolute;
}
.window-inner {
background-color: #20202c;
clip-path: var(--clip-window-inner);
}
.title-bar {
.title-bar-text {
margin-top: 2.5px !important;
}
user-select: none;
display: flex;
justify-content: space-between;
background-color: #313244;
border-radius: 15px 15px 0 0;
padding: 9px 18px 9px 18px;
align-items: stretch;
height: 42px;
padding-left: 1rem;
}
.title-bar span {
align-self: center;
}
svg {
fill: var(--text);
margin: 0;
padding: 0;
height: 24px !important;
height: 24px;
}
#close {
background: none;
@ -36,15 +43,16 @@ const randomID = customClass || Math.random().toString().replace(".", "");
border: none;
padding: 0;
margin: 0;
height: 24px !important;
width: 50px;
font: inherit;
cursor: pointer;
outline: inherit;
}
.window-body {
padding: 20px !important;
background-color: #1e1e2e;
border-radius: 0 0 17px 17px;
gap: 1rem;
display: flex;
padding: 0 1rem 1rem 1rem;
flex-direction: column;
}
@media (pointer: coarse) {
.window {
@ -64,28 +72,24 @@ const randomID = customClass || Math.random().toString().replace(".", "");
style={hideByDefault && "display: none;"}
>
<div class="window" style="max-width: 100%">
<div class="title-bar">
<div class="title-bar-text">{title}</div>
{
showClose && (
<button
id="close"
aria-label="Close"
onclick={`document.querySelector("#window-${randomID}").style.display = "none";`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
<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";`}
>
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
</svg>
</button>
)
}
</div>
<div class="window-body">
<slot />
<XmarkIcon width="12" height="12" />
</button>
)
}
</div>
<div class="window-body">
<slot />
</div>
</div>
</div>
</div>

View file

@ -1,6 +1,9 @@
---
import GroupBox from "@components/GroupBox.astro";
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
@ -11,24 +14,16 @@ import Window from "@components/Window.astro";
customClass="code-window"
offset="55px"
>
<p>
As mentioned in the main page, I write code. Some used by a lot, some
not as much. You can find it on:
<GroupBox name="GitHub (@nin0-dev)">
Here, you'll find most of my external contributions. I rarely start
personal projects on GitHub.
<br />
<a href="https://gh.nin0.dev">Go!</a>
<br />
<br />
</GroupBox>
<GroupBox name="Forgejo (@nin0)">
Some more personal/niche projects can be found on my own instance
(git.nin0.dev), which will probably be down.
<br />
<a href="https://git.nin0.dev/nin0">Go!</a>
<br />
<br />
</GroupBox>
</p>
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>

View file

@ -0,0 +1,21 @@
---
const { ...attrs } = Astro.props
---
<a {...attrs}>
<slot name="icon" />
<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>

View file

@ -1,22 +1,22 @@
---
import Social from "./Social.astro";
const contacts: any = Object.values(
import.meta.glob("../../../../info/contacts/*.md", {
eager: true
})
);
import Chip from "./Chip.astro";
import Envelope from "@assets/svg/envelope.svg";
import Plane from "@assets/svg/plane.svg";
---
<ul>
{
contacts.map(contact => (
<Social
platform={contact.frontmatter.platform}
url={contact.frontmatter.url}
name={contact.frontmatter.name}
note={contact.frontmatter.note}
/>
))
<div>
<Chip href="mailto:support@nin0.dev">
<Envelope slot="icon" />
support@nin0.dev
</Chip>
<Chip href="https://nin0dev.t.me">
<Plane slot="icon" />
@nin0dev
</Chip>
</div>
<style>
div {
display: flex;
flex-wrap: wrap;
}
</ul>
</style>

View file

@ -4,29 +4,22 @@ import GroupBox from "@components/GroupBox.astro";
import Me from "./Me";
import Window from "@components/Window.astro";
import Button from "@components/Button.astro";
import ButtonCollection from "@components/ButtonCollection.astro";
import Spacer from "@components/Spacer.astro";
---
<script>
import { WindowManager } from "@models/WindowManager";
document
.querySelector("#show-code-window")
.addEventListener("click", () => {
// @ts-ignore
document.querySelector("#window-code-window").style.display =
"block";
});
.addEventListener("click", () => WindowManager.topmost = document.getElementById("window-code-window"));
</script>
<Window title="Home" maxWidth="600px" showClose={false}>
<Me client:only />
<ButtonCollection>
<Button position="left" text="My code" id="show-code-window" />
</ButtonCollection>
<Button position="left" id="show-code-window">My code</Button>
<GroupBox name="About me">
<p>
I'm a Canadian self-taught software developer.
<br />
I also make things that some people use with varying degrees of usefulness.
</p>
</GroupBox>

View file

@ -1,12 +1,12 @@
import "@css/me.css";
import { initLanyard } from "@js/lanyard/lanyard";
import { type LanyardPresence } from "../../../js/lanyard/types";
import { useEffect, useState } from "react";
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 api_key = "3c5623fa1abbd11c49f53ca18e992ead";
const apiKey = "3c5623fa1abbd11c49f53ca18e992ead";
export default function Me() {
// me-e-eeeee
@ -21,19 +21,17 @@ export default function Me() {
loved: boolean;
streak: number;
spotifyURL?: string;
}
}
| undefined
>();
useEffect(() => {
initLanyard(presence => {
setPresence(presence);
});
new Lanyard("886685857560539176", p => setPresence(p));
async function getLastFmTracks() {
const params = new URLSearchParams({
method: "user.getrecenttracks",
api_key,
api_key: apiKey,
user: "nin0dev",
limit: "50",
format: "json",
@ -61,7 +59,7 @@ export default function Me() {
`https://ws.audioscrobbler.com/2.0/?${new URLSearchParams(
{
method: "track.getinfo",
api_key,
api_key: apiKey,
format: "json",
track: currentlyPlayingTrack.name,
artist: currentlyPlayingTrack.artist.name
@ -105,136 +103,141 @@ export default function Me() {
return (
<>
<div className="me-container">
<img
src="logo.png"
alt="the nin0 logo"
<div
className="img-container"
style={{
borderColor: presence
backgroundColor: presence
? ["online", "idle"].includes(
presence.discord_status
)
? "#a6e3a1"
: "#6c7086"
: "#6c7086"
? "var(--green)"
: "var(--overlay0)"
: "var(--overlay0)"
}}
/>
<h3>
nin0 <span style={{ fontSize: "0.9rem" }}>(he/him)</span>
</h3>
>
<img
src="logo.png"
alt="the nin0 logo"
/>
</div>
<div>
<h2>
nin0
</h2>
<h4>
he/him
</h4>
</div>
</div>
{currentlyPlaying && (
<div
className="spotify-card"
className="spotify-card-wrapper"
style={{
borderColor: lightenColor(
currentlyPlaying.mainColor,
90
)
}}
"--album-color": currentlyPlaying.mainColor,
"--album-art": `url(${currentlyPlaying.albumArt})`
} as React.CSSProperties}
>
<h3
style={{
color: lightenColor(currentlyPlaying.mainColor, 90)
}}
>
Listening to
</h3>
<div
className="spotify-card-background"
style={{
backgroundImage: `url(${currentlyPlaying.albumArt})`
}}
></div>
<div
className="spotify-card-content"
style={{
display: "flex",
alignItems: "center",
marginBottom: "10px"
}}
className="spotify-card"
>
<img
src={currentlyPlaying.albumArt}
<h3
style={{
marginRight: "10px",
width: "50px",
borderRadius: "5px"
color: lightenColor(currentlyPlaying.mainColor, 90)
}}
/>
<div>
<p
>
Listening to
</h3>
<div
className="spotify-card-content"
style={{
display: "flex",
alignItems: "center",
marginBottom: "10px"
}}
>
<img
src={currentlyPlaying.albumArt}
style={{
margin: "0",
fontWeight: "600",
color: lightenColor(
currentlyPlaying.mainColor,
90
)
marginRight: "10px",
width: "50px",
borderRadius: "5px"
}}
>
{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}
/>
<div>
<p
style={{
margin: "0",
fontWeight: "600",
color: lightenColor(
currentlyPlaying.mainColor,
90
)
}}
>
{flag.data}{" "}
{flags.indexOf(flag) !==
flags.length - 1 && (
<span style={{ color: "white" }}>
{" "}{" "}
</span>
)}
</span>
));
})()}
</p>
{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>
)}
</>

View file

@ -1,18 +0,0 @@
---
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>