initial commit

This commit is contained in:
laura 2025-10-27 04:10:26 -03:00
commit 30f2b4714d
Signed by: w
GPG key ID: BCD2117C99E69817
43 changed files with 3654 additions and 0 deletions

14
.gitignore vendored Normal file
View file

@ -0,0 +1,14 @@
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# Fresh build directory
_fresh/
# npm + other dependencies
node_modules/
vendor/
sync.sh

17
README.md Normal file
View file

@ -0,0 +1,17 @@
# Fresh project
Your new Fresh project is ready to go. You can follow the Fresh "Getting
Started" guide here: https://fresh.deno.dev/docs/getting-started
### Usage
Make sure to install Deno:
https://docs.deno.com/runtime/getting_started/installation
Then start the project in development mode:
```
deno task dev
```
This will watch the project directory and restart as necessary.

BIN
assets/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

552
assets/styles.css Normal file
View file

@ -0,0 +1,552 @@
@font-face {
font-family: "Jersey 15";
src: url("/fonts/jersey15.ttf") format("truetype");
}
@font-face {
font-family: "Share Tech";
src: url("/fonts/str.ttf") format("truetype");
}
:root {
--theme-bg: #16191c;
--theme-bg-secondary: #252a2e;
--theme-bg-tertiary: #1b1f22;
--theme-fg: #a3aaaf;
--theme-fg-alt-rgb: 106, 111, 121;
--theme-fg-alt: rgb(var(--theme-fg-alt-rgb));
--theme-accent-rgb: 163, 170, 175; /* 147, 184, 217; */
--theme-accent: rgb(var(--theme-accent-rgb));
--theme-accent-alt: #2c3237; /* #2a353e; */
--theme-accent-third: rgb(140, 155, 189); /* #2a353e; */
--theme-accent-title-rgb: var(--theme-accent-rgb);
--theme-accent-title: rgb(var(--theme-accent-title-rgb));
--theme-border-rgb: 56, 60, 66;
--theme-border: rgb(var(--theme-border-rgb));
}
html {
line-height: 1.5;
font-family: "Jersey 15", sans-serif, "Share Tech", monospace;
font-size: 22px;
-webkit-font-smoothing: none;
font-smooth: never;
}
body {
padding: 16px;
margin: auto;
background-color: var(--theme-bg);
color: var(--theme-fg);
}
.section {
display: flex;
flex-direction: column;
padding: 2.4rem;
overflow-wrap: break-word;
clip-path: polygon(
0px calc(100% - 8px),
8px calc(100% - 8px),
8px 100%,
calc(100% - 8px) 100%,
calc(100% - 8px) calc(100% - 8px),
100% calc(100% - 8px),
100% 8px,
calc(100% - 8px) 8px,
calc(100% - 8px) 0px,
8px 0px,
8px 8px,
0px 8px
);
box-sizing: border-box;
background-color: var(--theme-bg-secondary);
gap: 1rem;
}
.section,
#header {
max-width: 36rem;
width: 100%;
}
.alt {
color: var(--theme-fg-alt);
}
.alt-font {
font-size: 16px;
font-family: "Share Tech", monospace;
font-weight: 700;
letter-spacing: 0.05em;
}
.section p {
margin: 0;
}
p {
hyphens: auto;
}
.intro > .logo {
height: 1em;
margin-right: 0.5ch;
vertical-align: -4px;
}
.centered {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
gap: 1em;
}
.title {
color: var(--theme-accent-title);
padding-bottom: 0.25em;
border-bottom: 2px solid var(--theme-border);
text-align: center;
justify-content: center;
margin: 0px;
padding-bottom: 0.5rem;
display: flex;
align-items: center;
gap: 0.25em;
}
.title-1 {
font-size: 1.25rem;
}
.title-3 {
color: var(--theme-fg-alt);
padding-bottom: 0.16rem;
border-bottom: none;
font-size: 0.9rem;
}
.title.title-3::after {
background-image: linear-gradient(
90deg,
rgba(var(--theme-border-rgb), 0) 0,
rgba(var(--theme-fg-alt-rgb), 255)
);
}
.title::after {
background-image: linear-gradient(
90deg,
rgba(var(--theme-accent-title-rgb), 0) 0,
rgba(var(--theme-accent-title-rgb), 255)
);
content: "";
display: block;
flex: 1;
margin-left: 0.5ch;
height: 0.3rem;
border-radius: 3px;
}
.title .icon {
height: 24px;
width: 24px;
shape-rendering: crispEdges;
}
.box {
background-color: var(--theme-accent-alt);
color: var(--theme-accent);
padding: 0.5rem 1rem;
line-height: 1;
box-sizing: border-box;
width: fit-content;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
clip-path: polygon(
0px calc(100% - 6px),
6px calc(100% - 6px),
6px 100%,
calc(100% - 6px) 100%,
calc(100% - 6px) calc(100% - 6px),
100% calc(100% - 6px),
100% 6px,
calc(100% - 6px) 6px,
calc(100% - 6px) 0px,
6px 0px,
6px 6px,
0px 6px
);
display: flex;
align-items: center;
gap: 0.35rem;
}
a,
a:visited,
a:active {
color: var(--theme-accent-third);
text-decoration: none;
}
a:hover {
text-decoration: 2px underline;
text-underline-offset: 3px;
}
#header {
position: relative;
display: flex;
flex-direction: row;
justify-content: space-between;
z-index: 1;
}
#header,
#header #left,
#header #links {
flex-wrap: wrap;
}
#header #left,
#header #links {
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
margin: 0;
padding: 0;
}
#header img {
width: auto;
height: 1.5em;
}
nav .module {
clip-path: polygon(
0px calc(100% - 6px),
6px calc(100% - 6px),
6px 100%,
calc(100% - 6px) 100%,
calc(100% - 6px) calc(100% - 6px),
100% calc(100% - 6px),
100% 6px,
calc(100% - 6px) 6px,
calc(100% - 6px) 0px,
6px 0px,
6px 6px,
0px 6px
);
padding: 0.5em 1.25em;
box-sizing: border-box;
background-color: var(--theme-bg-secondary);
}
#logo {
display: flex;
align-items: center;
padding: 0.5em;
}
.labeled-icons {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 0.35rem;
margin-top: 0.25rem;
}
.labeled-icons.full {
flex-direction: column;
& > * {
flex-grow: 1;
}
& .box {
width: 100%;
}
}
.labeled-icons.b88x31 {
gap: 0.35rem;
}
.labeled-icons.b88x31 a {
display: flex;
width: 88px;
height: 31px;
background: var(--theme-bg-tertiary);
color: var(--theme-fg-alt);
justify-content: center;
}
.labeled-icons.b88x31 a,
#muxiepuff {
clip-path: polygon(
0px calc(100% - 3px),
3px calc(100% - 3px),
3px 100%,
calc(100% - 3px) 100%,
calc(100% - 3px) calc(100% - 3px),
100% calc(100% - 3px),
100% 3px,
calc(100% - 3px) 3px,
calc(100% - 3px) 0px,
3px 0px,
3px 3px,
0px 3px
);
}
.labeled-icons.b88x31 li {
transition: filter 0.2s ease;
}
.labeled-icons.b88x31 li:hover {
filter: brightness(0.5);
}
.labeled-icons.b88x31 img {
image-rendering: pixelated;
}
.labeled-icons.b88x31 img {
image-rendering: pixelated;
}
#extra {
display: flex;
list-style: none;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0;
}
#extra svg {
width: 15rem;
max-width: 100%;
height: 15rem;
}
@media (max-width: 768px) {
html {
font-size: 18px;
}
}
@media (max-width: 480px) {
html {
font-size: 16px;
}
body {
padding: 8px;
}
}
@media (max-width: 768px) {
.section {
padding: 1.5rem;
}
}
@media (max-width: 480px) {
.section {
padding: 1rem;
}
}
@media (max-width: 480px) {
.box {
padding: 0.4rem 0.8rem;
font-size: 0.9em;
}
.box svg {
width: 20px;
height: 20px;
}
}
@media (max-width: 480px) {
.title {
font-size: 1.1rem;
}
}
@media (max-width: 768px) {
#extra {
justify-content: center;
gap: 1rem;
}
#extra svg {
width: 8rem;
height: 8rem;
}
}
@media (max-width: 480px) {
#extra svg {
width: 6rem;
height: 6rem;
}
}
@media (max-width: 480px) {
.alt-font {
font-size: 14px;
}
}
#muxiepuff {
display: flex;
padding: 0;
background: none;
outline: none;
border: none;
cursor: pointer;
}
.name-scroller {
display: inline-block;
position: relative;
vertical-align: middle;
margin-top: -3px;
overflow: hidden;
}
.name-wrapper {
transition:
transform 0.3s ease-in-out,
opacity 0.3s ease-in-out;
transform: translateY(0);
opacity: 1;
}
.name-wrapper.animating {
transform: translateY(-100%);
opacity: 0;
}
.name-wrapper:not(.animating) {
transform: translateY(0);
opacity: 1;
}
.name-text {
position: relative;
display: inline-block;
}
.name-underline {
position: absolute;
bottom: 2px;
left: 0;
width: 100%;
height: 2px;
background: currentColor;
box-shadow: 0 0 8px currentColor;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.inline-code {
font-family: "Share Tech Mono", "Share Tech", monospace;
font-size: 0.75em;
background-color: var(--theme-border);
padding: 1px;
display: inline-block;
clip-path: polygon(
0px calc(100% - 2px),
2px calc(100% - 2px),
2px 100%,
calc(100% - 2px) 100%,
calc(100% - 2px) calc(100% - 2px),
100% calc(100% - 2px),
100% 2px,
calc(100% - 2px) 2px,
calc(100% - 2px) 0px,
2px 0px,
2px 2px,
0px 2px
);
line-height: 1.6;
vertical-align: middle;
}
.inline-code > span {
display: block;
background-color: var(--theme-bg-tertiary);
color: var(--theme-fg);
padding: 0.15em 0.4em;
clip-path: polygon(
0px calc(100% - 2px),
2px calc(100% - 2px),
2px 100%,
calc(100% - 2px) 100%,
calc(100% - 2px) calc(100% - 2px),
100% calc(100% - 2px),
100% 2px,
calc(100% - 2px) 2px,
calc(100% - 2px) 0px,
2px 0px,
2px 2px,
0px 2px
);
font-weight: 500;
}
p,
.title {
transform: translateZ(0px);
-webkit-transform: translateZ(0px);
will-change: transform;
}
hr {
width: 100%;
border: none;
border-top: 2px solid var(--theme-border);
}
footer.section {
display: flex;
align-items: center;
justify-content: center;
gap: 1ch;
}
footer p {
text-align: center;
gap: 1ch;
margin: 0;
padding: 0;
}
footer a {
overflow-wrap: anywhere;
word-break: break-word;
}
footer #f-heart {
color: var(--theme-accent-third);
vertical-align: middle;
}

7
client.ts Normal file
View file

@ -0,0 +1,7 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
// Import CSS files here for hot module reloading to work.
import "./assets/styles.css";

22
components/Box.tsx Normal file
View file

@ -0,0 +1,22 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { ComponentChildren, h } from "preact";
import { LinkIcon } from "./Icon.tsx";
interface PolygonBoxProps {
as?: keyof HTMLElementTagNameMap;
children?: ComponentChildren;
[key: string]: any;
}
export default function Box(
{ as: Tag = "span", class: className, children, ...props }: PolygonBoxProps,
) {
const content = Tag === "a"
? [children, h(LinkIcon, { size: 16, class: "link-icon" })]
: children;
return h(Tag, { class: `box ${className || ""}`, ...props }, content);
}

29
components/Footer.tsx Normal file
View file

@ -0,0 +1,29 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { HeartFilledIcon } from "./Icon.tsx";
import Link from "./Link.tsx";
export default function Footer() {
return (
<footer class="section">
<p class="alt alt-font">
Made with <HeartFilledIcon id="f-heart" /> · Source code available at{" "}
<Link href="https://git.acpi.at/mux/web">git.acpi.at</Link> under the{" "}
<Link href="https://spdx.org/licenses/AGPL-3.0-or-later.html">
GNU Affero General Public License v3.0
</Link>
, with all site content licensed under{" "}
<Link href="https://creativecommons.org/licenses/by-sa/4.0/">
CC BY-SA 4.0
</Link>
.
</p>
<p class="alt alt-font">
© 2025 muxiepuff Powered by FreeBSD and pixels
</p>
</footer>
);
}

35
components/Header.tsx Normal file
View file

@ -0,0 +1,35 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
export default function Header() {
return (
<nav id="header">
<div id="left">
<a id="logo" href="/" class="module">
<img
src="/bnuy.webp"
width="380"
height="260"
alt="acpi.at"
style="filter: grayscale(0.6)"
/>
</a>
{
// <ul id="links">
// <li class="module">
// <a href="/projects">Projects</a>
// </li>
// <li class="module">
// <a href="/blog">Blog</a>
// </li>
// </ul>
}
</div>
{
// <div id="right" class="module" />
}
</nav>
);
}

442
components/Icon.tsx Normal file
View file

@ -0,0 +1,442 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
interface IconProps {
size?: number;
[key: string]: any;
}
export function LinkIcon({ size = 24, ...props }: IconProps) {
return (
<svg
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path
d="M21 11V3h-8v2h4v2h-2v2h-2v2h-2v2H9v2h2v-2h2v-2h2V9h2V7h2v4h2zM11 5H3v16h16v-8h-2v6H5V7h6V5z"
fill="currentColor"
/>
</svg>
);
}
export function PersonIcon({ size = 24, ...props }: IconProps) {
return (
<svg
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path
d="M10 2h4v4h-4V2zM7 7h10v2h-2v13h-2v-6h-2v6H9V9H7V7zM5 5v2h2V5H5zm0 0H3V3h2v2zm14 0v2h-2V5h2zm0 0V3h2v2h-2z"
fill="currentColor"
/>
</svg>
);
}
export function HeartIcon({ size = 24, ...props }: IconProps) {
return (
<svg
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path
d="M9 2H5v2H3v2H1v6h2v2h2v2h2v2h2v2h2v2h2v-2h2v-2h2v-2h2v-2h2v-2h2V6h-2V4h-2V2h-4v2h-2v2h-2V4H9V2zm0 2v2h2v2h2V6h2V4h4v2h2v6h-2v2h-2v2h-2v2h-2v2h-2v-2H9v-2H7v-2H5v-2H3V6h2V4h4z"
fill="currentColor"
/>
</svg>
);
}
export function CommentIcon({ size = 24, ...props }: IconProps) {
return (
<svg
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path
d="M22 2H2v14h2V4h16v12h-8v2h-2v2H8v-4H2v2h4v4h4v-2h2v-2h10V2z"
fill="currentColor"
/>
</svg>
);
}
export function AttachmentIcon({ size = 24, ...props }: IconProps) {
return (
<svg
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path
d="M7 5v14H5V3h14v18H9V7h6v10h-2V9h-2v10h6V5H7z"
fill="currentColor"
/>
</svg>
);
}
export function LabelIcon({ size = 24, ...props }: IconProps) {
return (
<svg
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path
d="M12 2H2v10h2v2h2v2h2v2h2v2h2v2h2v-2h2v-2h2v-2h2v-2h2v-2h-2v-2h-2V8h-2V6h-2V4h-2V2zm0 2v2h2v2h2v2h2v2h2v2h-2v2h-2v2h-2v2h-2v-2h-2v-2H8v-2H6v-2H4V4h8zM6 6h2v2H6V6z"
fill="currentColor"
/>
</svg>
);
}
export function DiscordIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Discord</title>
<path
d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"
fill="currentColor"
/>
</svg>
);
}
export function SignalIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Signal</title>
<path
d="M12 0q-.934 0-1.83.139l.17 1.111a11 11 0 0 1 3.32 0l.172-1.111A12 12 0 0 0 12 0M9.152.34A12 12 0 0 0 5.77 1.742l.584.961a10.8 10.8 0 0 1 3.066-1.27zm5.696 0-.268 1.094a10.8 10.8 0 0 1 3.066 1.27l.584-.962A12 12 0 0 0 14.848.34M12 2.25a9.75 9.75 0 0 0-8.539 14.459c.074.134.1.292.064.441l-1.013 4.338 4.338-1.013a.62.62 0 0 1 .441.064A9.7 9.7 0 0 0 12 21.75c5.385 0 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25m-7.092.068a12 12 0 0 0-2.59 2.59l.909.664a11 11 0 0 1 2.345-2.345zm14.184 0-.664.909a11 11 0 0 1 2.345 2.345l.909-.664a12 12 0 0 0-2.59-2.59M1.742 5.77A12 12 0 0 0 .34 9.152l1.094.268a10.8 10.8 0 0 1 1.269-3.066zm20.516 0-.961.584a10.8 10.8 0 0 1 1.27 3.066l1.093-.268a12 12 0 0 0-1.402-3.383M.138 10.168A12 12 0 0 0 0 12q0 .934.139 1.83l1.111-.17A11 11 0 0 1 1.125 12q0-.848.125-1.66zm23.723.002-1.111.17q.125.812.125 1.66c0 .848-.042 1.12-.125 1.66l1.111.172a12.1 12.1 0 0 0 0-3.662M1.434 14.58l-1.094.268a12 12 0 0 0 .96 2.591l-.265 1.14 1.096.255.36-1.539-.188-.365a10.8 10.8 0 0 1-.87-2.35m21.133 0a10.8 10.8 0 0 1-1.27 3.067l.962.584a12 12 0 0 0 1.402-3.383zm-1.793 3.848a11 11 0 0 1-2.345 2.345l.664.909a12 12 0 0 0 2.59-2.59zm-19.959 1.1L.357 21.48a1.8 1.8 0 0 0 2.162 2.161l1.954-.455-.256-1.095-1.953.455a.675.675 0 0 1-.81-.81l.454-1.954zm16.832 1.769a10.8 10.8 0 0 1-3.066 1.27l.268 1.093a12 12 0 0 0 3.382-1.402zm-10.94.213-1.54.36.256 1.095 1.139-.266c.814.415 1.683.74 2.591.961l.268-1.094a10.8 10.8 0 0 1-2.35-.869zm3.634 1.24-.172 1.111a12.1 12.1 0 0 0 3.662 0l-.17-1.111q-.812.125-1.66.125a11 11 0 0 1-1.66-.125"
fill="currentColor"
/>
</svg>
);
}
export function LastfmIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Last.fm</title>
<path
d="M10.584 17.21l-.88-2.392s-1.43 1.594-3.573 1.594c-1.897 0-3.244-1.649-3.244-4.288 0-3.382 1.704-4.591 3.381-4.591 2.42 0 3.189 1.567 3.849 3.574l.88 2.749c.88 2.666 2.529 4.81 7.285 4.81 3.409 0 5.718-1.044 5.718-3.793 0-2.227-1.265-3.381-3.63-3.931l-1.758-.385c-1.21-.275-1.567-.77-1.567-1.595 0-.934.742-1.484 1.952-1.484 1.32 0 2.034.495 2.144 1.677l2.749-.33c-.22-2.474-1.924-3.492-4.729-3.492-2.474 0-4.893.935-4.893 3.932 0 1.87.907 3.051 3.189 3.601l1.87.44c1.402.33 1.869.907 1.869 1.704 0 1.017-.99 1.43-2.86 1.43-2.776 0-3.93-1.457-4.59-3.464l-.907-2.75c-1.155-3.573-2.997-4.893-6.653-4.893C2.144 5.333 0 7.89 0 12.233c0 4.18 2.144 6.434 5.993 6.434 3.106 0 4.591-1.457 4.591-1.457z"
fill="currentColor"
/>
</svg>
);
}
export function BlueskyIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Bluesky</title>
<path
d="M5.202 2.857C7.954 4.922 10.913 9.11 12 11.358c1.087-2.247 4.046-6.436 6.798-8.501C20.783 1.366 24 .213 24 3.883c0 .732-.42 6.156-.667 7.037-.856 3.061-3.978 3.842-6.755 3.37 4.854.826 6.089 3.562 3.422 6.299-5.065 5.196-7.28-1.304-7.847-2.97-.104-.305-.152-.448-.153-.327 0-.121-.05.022-.153.327-.568 1.666-2.782 8.166-7.847 2.97-2.667-2.737-1.432-5.473 3.422-6.3-2.777.473-5.899-.308-6.755-3.369C.42 10.04 0 4.615 0 3.883c0-3.67 3.217-2.517 5.202-1.026"
fill="currentColor"
/>
</svg>
);
}
export function MastodonIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Mastodon / Fediverse</title>
<path
d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"
fill="currentColor"
/>
</svg>
);
}
export function ForgejoIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Forgejo</title>
<path
d="M16.7773 0c1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.1175a7.0759 7.0759 0 0 1 4.148-1.4205l.1176-.001 1.3385.0002c.4973-.8827 1.4434-1.4788 2.5288-1.4788 1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.319c.8827.4973 1.4788 1.4434 1.4788 2.5287 0 1.602-1.2986 2.9005-2.9005 2.9005-1.6018 0-2.9004-1.2986-2.9004-2.9005 0-1.0853.596-2.0314 1.4788-2.5287l-.0002-9.9831c0-3.887 3.1195-7.0453 6.9915-7.108l.1176-.001h1.3385C14.7458.5962 15.692 0 16.7773 0ZM7.2227 19.9052c-.6596 0-1.1943.5347-1.1943 1.1943s.5347 1.1943 1.1943 1.1943 1.1944-.5347 1.1944-1.1943-.5348-1.1943-1.1944-1.1943Zm9.5546-10.4644c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Zm0-7.7346c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Z"
fill="currentColor"
/>
</svg>
);
}
export function CodebergIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Codeberg</title>
<path
d="M11.999.747A11.974 11.974 0 0 0 0 12.75c0 2.254.635 4.465 1.833 6.376L11.837 6.19c.072-.092.251-.092.323 0l4.178 5.402h-2.992l.065.239h3.113l.882 1.138h-3.674l.103.374h3.86l.777 1.003h-4.358l.135.483h4.593l.695.894h-5.038l.165.589h5.326l.609.785h-5.717l.182.65h6.038l.562.727h-6.397l.183.65h6.717A12.003 12.003 0 0 0 24 12.75 11.977 11.977 0 0 0 11.999.747zm3.654 19.104.182.65h5.326c.173-.204.353-.433.513-.65zm.385 1.377.18.65h3.563c.233-.198.485-.428.712-.65zm.383 1.377.182.648h1.203c.356-.204.685-.412 1.042-.648zz"
fill="currentColor"
/>
</svg>
);
}
export function GitHubIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>GitHub</title>
<path
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
fill="currentColor"
/>
</svg>
);
}
export function MailIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
width={size}
height={size}
{...props}
>
<path
d="M3 4a2 2 0 0 0-2 2v1.161l8.441 4.221a1.25 1.25 0 0 0 1.118 0L19 7.162V6a2 2 0 0 0-2-2H3Z"
fill="currentColor"
/>
<path
d="m19 8.839-7.77 3.885a2.75 2.75 0 0 1-2.46 0L1 8.839V14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8.839Z"
fill="currentColor"
/>
</svg>
);
}
export function TwitterIcon({ size = 24, ...props }: IconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path
d="M21.543 7.104c.015.211.015.423.015.636 0 6.507-4.954 14.01-14.01 14.01v-.003A13.94 13.94 0 0 1 0 19.539a9.88 9.88 0 0 0 7.287-2.041 4.93 4.93 0 0 1-4.6-3.42 4.916 4.916 0 0 0 2.223-.084A4.926 4.926 0 0 1 .96 9.167v-.062a4.887 4.887 0 0 0 2.235.616A4.928 4.928 0 0 1 1.67 3.148 13.98 13.98 0 0 0 11.82 8.292a4.929 4.929 0 0 1 8.39-4.49 9.868 9.868 0 0 0 3.128-1.196 4.941 4.941 0 0 1-2.165 2.724A9.828 9.828 0 0 0 24 4.555a10.019 10.019 0 0 1-2.457 2.549z"
fill="currentColor"
/>
</svg>
);
}
export function KofiIcon({ size = 24, ...props }: IconProps) {
return (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Ko-fi</title>
<path
d="M11.351 2.715c-2.7 0-4.986.025-6.83.26C2.078 3.285 0 5.154 0 8.61c0 3.506.182 6.13 1.585 8.493 1.584 2.701 4.233 4.182 7.662 4.182h.83c4.209 0 6.494-2.234 7.637-4a9.5 9.5 0 0 0 1.091-2.338C21.792 14.688 24 12.22 24 9.208v-.415c0-3.247-2.13-5.507-5.792-5.87-1.558-.156-2.65-.208-6.857-.208m0 1.947c4.208 0 5.09.052 6.571.182 2.624.311 4.13 1.584 4.13 4v.39c0 2.156-1.792 3.844-3.87 3.844h-.935l-.156.649c-.208 1.013-.597 1.818-1.039 2.546-.909 1.428-2.545 3.064-5.922 3.064h-.805c-2.571 0-4.831-.883-6.078-3.195-1.09-2-1.298-4.155-1.298-7.506 0-2.181.857-3.402 3.012-3.714 1.533-.233 3.559-.26 6.39-.26m6.547 2.287c-.416 0-.65.234-.65.546v2.935c0 .311.234.545.65.545 1.324 0 2.051-.754 2.051-2s-.727-2.026-2.052-2.026m-10.39.182c-1.818 0-3.013 1.48-3.013 3.142 0 1.533.858 2.857 1.949 3.897.727.701 1.87 1.429 2.649 1.896a1.47 1.47 0 0 0 1.507 0c.78-.467 1.922-1.195 2.623-1.896 1.117-1.039 1.974-2.364 1.974-3.897 0-1.662-1.247-3.142-3.039-3.142-1.065 0-1.792.545-2.338 1.298-.493-.753-1.246-1.298-2.312-1.298"
fill="currentColor"
/>
</svg>
);
}
export function BitcoinIcon({ size = 24, ...props }: IconProps) {
return (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Bitcoin</title>
<path
d="M23.638 14.904c-1.602 6.43-8.113 10.34-14.542 8.736C2.67 22.05-1.244 15.525.362 9.105 1.962 2.67 8.475-1.243 14.9.358c6.43 1.605 10.342 8.115 8.738 14.548v-.002zm-6.35-4.613c.24-1.59-.974-2.45-2.64-3.03l.54-2.153-1.315-.33-.525 2.107c-.345-.087-.705-.167-1.064-.25l.526-2.127-1.32-.33-.54 2.165c-.285-.067-.565-.132-.84-.2l-1.815-.45-.35 1.407s.975.225.955.236c.535.136.63.486.615.766l-1.477 5.92c-.075.166-.24.406-.614.314.015.02-.96-.24-.96-.24l-.66 1.51 1.71.426.93.242-.54 2.19 1.32.327.54-2.17c.36.1.705.19 1.05.273l-.51 2.154 1.32.33.545-2.19c2.24.427 3.93.257 4.64-1.774.57-1.637-.03-2.58-1.217-3.196.854-.193 1.5-.76 1.68-1.93h.01zm-3.01 4.22c-.404 1.64-3.157.75-4.05.53l.72-2.9c.896.23 3.757.67 3.33 2.37zm.41-4.24c-.37 1.49-2.662.735-3.405.55l.654-2.64c.744.18 3.137.524 2.75 2.084v.006z"
fill="currentColor"
/>
</svg>
);
}
export function BitcoinCashIcon({ size = 24, ...props }: IconProps) {
return (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Bitcoin Cash</title>
<path
d="m10.84 11.22-.688-2.568c.728-.18 2.839-1.051 3.39.506.27 1.682-1.978 1.877-2.702 2.062zm.289 1.313.755 2.829c.868-.228 3.496-.46 3.241-2.351-.433-1.666-3.125-.706-3.996-.478zM24 12c0 6.627-5.373 12-12 12S0 18.627 0 12 5.373 0 12 0s12 5.373 12 12zm-6.341.661c-.183-1.151-1.441-2.095-2.485-2.202.643-.57.969-1.401.57-2.488-.603-1.368-1.989-1.66-3.685-1.377l-.546-2.114-1.285.332.536 2.108c-.338.085-.685.158-1.029.256L9.198 5.08l-1.285.332.545 2.114c-.277.079-2.595.673-2.595.673l.353 1.377s.944-.265.935-.244c.524-.137.771.125.886.372l1.498 5.793c.018.168-.012.454-.372.551.021.012-.935.241-.935.241l.14 1.605s2.296-.588 2.598-.664l.551 2.138 1.285-.332-.551-2.153c.353-.082.697-.168 1.032-.256l.548 2.141 1.285-.332-.551-2.135c1.982-.482 3.38-1.73 3.094-3.64z"
fill="currentColor"
/>
</svg>
);
}
export function MoneroIcon({ size = 24, ...props }: IconProps) {
return (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Monero</title>
<path
d="M12 0C5.365 0 0 5.373 0 12.015c0 1.335.228 2.607.618 3.81h3.577V5.729L12 13.545l7.805-7.815v10.095h3.577c.389-1.203.618-2.475.618-3.81C24 5.375 18.635 0 12 0zm-1.788 15.307l-3.417-3.421v6.351H1.758C3.87 21.689 7.678 24 12 24s8.162-2.311 10.245-5.764h-5.04v-6.351l-3.386 3.421-1.788 1.79-1.814-1.79h-.005z"
fill="currentColor"
/>
</svg>
);
}
export function NanoIcon({ size = 24, ...props }: IconProps) {
return (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Nano</title>
<path
d="m3.723 0 6.875 10.76H4.775v1.365h5.881l-1.76 2.73h-4.12v1.364h3.242L3.006 24h1.85l5.068-7.781h4.215L19.129 24h1.865l-4.941-7.781h3.232v-1.364h-4.1l-1.732-2.73h5.832V10.76h-5.803L20.45 0h-1.785l-6.588 10.107L5.627 0H3.723zm8.324 12.959 1.217 1.896h-2.451l1.234-1.896z"
fill="currentColor"
/>
</svg>
);
}
export function LitecoinIcon({ size = 24, ...props }: IconProps) {
return (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Litecoin</title>
<path
d="M12 0a12 12 0 1012 12A12 12 0 0012 0zm-.2617 3.6777h2.584a.3425.3425 0 01.33.4356l-2.0312 6.918 1.9062-.582-.4082 1.3847-1.9238.5605-1.248 4.213h6.6757a.3425.3425 0 01.3282.4374l-.582 2a.4586.4586 0 01-.4395.3301H6.7324l1.7227-5.8223-1.9063.5801.42-1.3613 1.9101-.58 2.4219-8.1798a.4557.4557 0 01.4375-.334Z"
fill="currentColor"
/>
</svg>
);
}
export function EthereumIcon({ size = 24, ...props }: IconProps) {
return (
<svg
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<title>Ethereum</title>
<path
d="M11.944 17.97L4.58 13.62 11.943 24l7.37-10.38-7.372 4.35h.003zM12.056 0L4.69 12.223l7.365 4.354 7.365-4.35L12.056 0z"
fill="currentColor"
/>
</svg>
);
}
export function HeartFilledIcon({ size = 24, ...props }: IconProps) {
return (
<svg
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width={size}
height={size}
{...props}
>
<path d="M9 2H5v2H3v2H1v6h2v2h2v2h2v2h2v2h2v2h2v-2h2v-2h2v-2h2v-2h2v-2h2V6h-2V4h-2V2h-4v2h-2v2h-2V4H9V2z" />
</svg>
);
}

32
components/Layout.tsx Normal file
View file

@ -0,0 +1,32 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { ComponentChildren } from "preact";
interface LayoutProps {
children: ComponentChildren;
[key: string]: any;
}
export function Section({ children, class: className, ...props }: LayoutProps) {
return (
<div
{...props}
class={`section ${className || ""}`}
>
{children}
</div>
);
}
export function Centered(
{ children, class: className, ...props }: LayoutProps,
) {
return (
<div class={`centered ${className || ""}`} {...props}>
{children}
</div>
);
}

24
components/Link.tsx Normal file
View file

@ -0,0 +1,24 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { HTMLAttributes, JSX } from "preact";
import { LinkIcon } from "./Icon.tsx";
interface LinkProps extends HTMLAttributes<HTMLAnchorElement> {
href: string;
children: JSX.Element | JSX.Element[] | string;
noIcon?: boolean;
}
export default function Link(
{ children, noIcon = false, ...props }: LinkProps,
) {
return (
<a {...props}>
{children}
{!noIcon && <LinkIcon size={16} class="link-icon" />}
</a>
);
}

60
components/Tinted.tsx Normal file
View file

@ -0,0 +1,60 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
interface TintedImageProps {
src: string;
colour: string;
[key: string]: any;
}
export default function TintedImage({
colour,
src,
...props
}: TintedImageProps) {
return (
<svg
width="100%"
height="100%"
{...props}
>
<defs>
<filter
id="tint"
x="0%"
y="0%"
width="100%"
height="100%"
colorInterpolationFilters="sRGB"
>
<feColorMatrix
type="saturate"
values="0"
result="grayscale"
/>
<feFlood flood-color={colour} result="tint" />
<feComposite
in="tint"
in2="grayscale"
operator="arithmetic"
k1="1"
k2="0"
k3="0"
k4="0"
/>
</filter>
</defs>
<image
x="0"
y="0"
width="100%"
height="100%"
preserveAspectRatio="xMidYMid meet"
filter={`url(#tint)`}
href={src}
/>
</svg>
);
}

29
components/Title.tsx Normal file
View file

@ -0,0 +1,29 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { ComponentChildren, h } from "preact";
interface TitleProps {
level?: 1 | 2 | 3 | 4 | 5 | 6;
children: ComponentChildren;
[key: string]: any;
}
export default function Title({
level = 1,
children,
class: className,
...props
}: TitleProps) {
const Heading = `h${Math.min(Math.max(level, 1), 6)}`;
return h(
Heading,
{
class: `title title-${level} ${className || ""}`,
...props,
},
children,
);
}

58
deno.json Normal file
View file

@ -0,0 +1,58 @@
{
"nodeModulesDir": "auto",
"tasks": {
"check": "deno fmt --check . && deno lint . && deno check",
"dev": "vite",
"build": "vite build",
"start": "deno serve -A _fresh/server.js",
"update": "deno run -A -r jsr:@fresh/update ."
},
"lint": {
"rules": {
"tags": [
"fresh",
"recommended"
],
"exclude": ["no-explicit-any", "no-window", "no-window-prefix"]
}
},
"exclude": [
"**/_fresh/*"
],
"imports": {
"fresh": "jsr:@fresh/core@^2.1.2",
"preact": "npm:preact@^10.27.2",
"@preact/signals": "npm:@preact/signals@^2.3.1",
"@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1.0.5",
"vite": "npm:vite@^7.1.3"
},
"compilerOptions": {
"lib": [
"dom",
"dom.asynciterable",
"dom.iterable",
"deno.ns"
],
"jsx": "precompile",
"jsxImportSource": "preact",
"jsxPrecompileSkipElements": [
"a",
"img",
"source",
"body",
"html",
"head",
"title",
"meta",
"script",
"link",
"style",
"base",
"noscript",
"template"
],
"types": [
"vite/client"
]
}
}

1123
deno.lock generated Normal file

File diff suppressed because it is too large Load diff

30
islands/Code.tsx Normal file
View file

@ -0,0 +1,30 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { ComponentChildren } from "preact";
interface CodeProps {
children: ComponentChildren;
[key: string]: any;
}
export default function Code({ children, ...props }: CodeProps) {
const handleClick = () => {
if (typeof children === "string") {
navigator.clipboard.writeText(children);
}
};
return (
<code
class="inline-code"
onClick={handleClick}
style={{ cursor: "pointer" }}
title={"Click to copy"}
{...props}
>
<span>{children}</span>
</code>
);
}

19
islands/Meow.tsx Normal file
View file

@ -0,0 +1,19 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
export default function Meow() {
return (
<button
type="button"
id="muxiepuff"
onClick={() =>
navigator.clipboard.writeText(
'<a href="https://acpi.at"><img src="https://acpi.at/88x31.gif" title="muxiepuff" alt="acpi.at" /></a>',
)}
>
<img src="/88x31.gif" title="muxiepuff (Click to copy to clipboard!)" alt="acpi.at" />
</button>
);
}

37
islands/Name.tsx Normal file
View file

@ -0,0 +1,37 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { useEffect, useState } from "preact/hooks";
export default function Name() {
const names = ["muxiepuff", "lívia", "aury"];
const ipas = ["/ˈmuˌksipʌf/", "/li.vjɐ/", "/ˈaʊ̯ˌɾi/"]
;
const [currentIndex, setCurrentIndex] = useState(0);
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
setIsAnimating(true);
setTimeout(() => {
setCurrentIndex((prev) => (prev + 1) % names.length);
setIsAnimating(false);
}, 200);
}, 2000);
return () => clearInterval(interval);
}, []);
return (
<span class="name-scroller">
<span class={`name-wrapper ${isAnimating ? "animating" : ""}`}>
<span class="name-text">
{names[currentIndex]}
<span class="name-underline"></span>
</span>{" "}
<span class="alt alt-font">({ipas[currentIndex]}, she/her)</span>
</span>
</span>
);
}

374
islands/Rain.tsx Normal file
View file

@ -0,0 +1,374 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { useEffect, useRef } from "preact/hooks";
interface Particle {
offsetX: number;
offsetY: number;
velocityX: number;
velocityY: number;
}
interface Raindrop {
x: number;
y: number;
length: number;
speed: number;
opacity: number;
floorHeight: number;
}
interface Splash {
x: number;
y: number;
age: number;
maxAge: number;
particles: Particle[];
}
interface Star {
x: number;
y: number;
size: number;
brightness: number;
twinkleSpeed: number;
twinkleOffset: number;
age: number;
maxAge: number;
canVanish: boolean;
color: { r: number; g: number; b: number };
colorIndex: number;
}
const PIXEL_SIZE = 4;
const DROP_COUNT = 300;
const RAIN_COLOR = { r: 87, g: 97, b: 100 };
const BACKGROUND_COLOUR = "#16191C";
const STAR_COUNT = 150;
const RAIN_SPEED = 0.75;
const SPLASH_CHANCE = 0.3;
const FLOOR_HEIGHT_MIN = 0.55;
const FLOOR_HEIGHT_RANGE = 0.95;
const PARTICLE_OFFSET_RANGE = 4;
const PARTICLE_Y_RANGE = 2;
const PARTICLE_Y_MIN = 1;
const PARTICLE_VX_RANGE = 0.8;
const PARTICLE_VY_RANGE = 0.5;
const PARTICLE_VY_MIN = 0.3;
const PARTICLE_GRAVITY = 0.15;
const STAR_BRIGHTNESS_MIN = 0.6;
const STAR_BRIGHTNESS_RANGE = 0.4;
const STAR_LARGE_THRESHOLD = 0.7;
const STAR_SIZE_SMALL = 4;
const STAR_SIZE_LARGE = 8;
const STAR_TWINKLE_MIN = 0.002;
const STAR_TWINKLE_RANGE = 0.005;
const STAR_AGE_MIN = 60;
const STAR_AGE_RANGE = 120;
const STAR_TWINKLE_AMPLITUDE = 0.3;
const STAR_TWINKLE_BASE = 0.7;
const STAR_GLOW_THRESHOLD = 0.7;
const STAR_GLOW_ALPHA = 0.5;
const RAIN_COLORS: string[] = [];
const STAR_COLORS: string[] = [];
const ALPHA_STAR_COLORS: string[] = [];
const NUM_COLOR_VARIATIONS = 20;
for (let i = 0; i < NUM_COLOR_VARIATIONS; i++) {
const brightness = 1 - (i / NUM_COLOR_VARIATIONS) * 0.5;
RAIN_COLORS.push(
`rgb(${Math.floor(RAIN_COLOR.r * brightness)},${
Math.floor(RAIN_COLOR.g * brightness)
},${Math.floor(RAIN_COLOR.b * brightness)})`,
);
}
for (let i = 0; i < NUM_COLOR_VARIATIONS; i++) {
const brightness = STAR_BRIGHTNESS_MIN +
(i / NUM_COLOR_VARIATIONS) * STAR_BRIGHTNESS_RANGE;
STAR_COLORS.push(
`rgb(${Math.floor(RAIN_COLOR.r * brightness)},${
Math.floor(RAIN_COLOR.g * brightness)
},${Math.floor(RAIN_COLOR.b * brightness)})`,
);
ALPHA_STAR_COLORS.push(
`rgba(${Math.floor(RAIN_COLOR.r * brightness)},${
Math.floor(RAIN_COLOR.g * brightness)
},${Math.floor(RAIN_COLOR.b * brightness)},`,
);
}
const SPLASH_PARTICLE_COLOR = `rgb(${RAIN_COLOR.r * 0.9},${
RAIN_COLOR.g * 0.95
},${RAIN_COLOR.b * 1.1})`;
const SPLASH_RIPPLE_COLOR = `rgb(${RAIN_COLOR.r * 0.7},${RAIN_COLOR.g * 0.8},${
RAIN_COLOR.b * 0.95
})`;
function createParticle(): Particle {
return {
offsetX: (Math.random() - 0.5) * PARTICLE_OFFSET_RANGE,
offsetY: -(Math.random() * PARTICLE_Y_RANGE + PARTICLE_Y_MIN),
velocityX: (Math.random() - 0.5) * PARTICLE_VX_RANGE,
velocityY: -(Math.random() * PARTICLE_VY_RANGE + PARTICLE_VY_MIN),
};
}
function createSplash(x: number, y: number): Splash {
return {
x,
y,
age: 0,
maxAge: 12,
particles: Array.from(
{ length: Math.floor(Math.random() * 3) + 3 },
createParticle,
),
};
}
function createRaindrop(gridWidth: number, gridHeight: number): Raindrop {
const bias = Math.random() * Math.random();
return {
x: Math.floor(Math.random() * gridWidth),
y: Math.floor(Math.random() * gridHeight) - gridHeight,
length: Math.floor(Math.random() * 4) + 2,
speed: RAIN_SPEED * (Math.random() * 2 + 1),
opacity: Math.random() * 0.3 + 0.6,
floorHeight: window.innerHeight *
(FLOOR_HEIGHT_MIN + bias * FLOOR_HEIGHT_RANGE),
};
}
function createStar(): Star {
const brightness = Math.random() * STAR_BRIGHTNESS_RANGE +
STAR_BRIGHTNESS_MIN;
const colorIndex = Math.floor(
(brightness - STAR_BRIGHTNESS_MIN) / STAR_BRIGHTNESS_RANGE *
(NUM_COLOR_VARIATIONS - 1),
);
return {
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
size: Math.random() > STAR_LARGE_THRESHOLD
? STAR_SIZE_LARGE
: STAR_SIZE_SMALL,
twinkleSpeed: Math.random() * STAR_TWINKLE_RANGE + STAR_TWINKLE_MIN,
twinkleOffset: Math.random() * Math.PI * 2,
brightness,
age: 0,
maxAge: STAR_AGE_MIN + Math.random() * STAR_AGE_RANGE,
canVanish: false,
colorIndex,
};
}
function updateParticle(p: Particle) {
p.offsetX += p.velocityX;
p.offsetY += p.velocityY;
p.velocityY += PARTICLE_GRAVITY;
}
function updateSplash(splash: Splash) {
splash.age++;
splash.particles.forEach(updateParticle);
}
function updateRaindrop(
drop: Raindrop,
splashes: Splash[],
gridWidth: number,
gridHeight: number,
) {
drop.y += drop.speed;
const dropY = drop.y * PIXEL_SIZE;
if (dropY >= drop.floorHeight && Math.random() < SPLASH_CHANCE) {
splashes.push(createSplash(drop.x, Math.floor(dropY / PIXEL_SIZE)));
Object.assign(drop, createRaindrop(gridWidth, gridHeight));
} else if (dropY > window.innerHeight) {
Object.assign(drop, createRaindrop(gridWidth, gridHeight));
}
}
function updateStar(star: Star): void {
star.age++;
const phase = (star.age * star.twinkleSpeed + star.twinkleOffset) %
(2 * Math.PI);
if (!star.canVanish && phase < star.twinkleSpeed) {
star.canVanish = true;
}
if (star.canVanish && star.age > star.maxAge) {
Object.assign(star, createStar());
} else if (star.x > window.innerWidth || star.y > window.innerHeight) {
Object.assign(star, createStar());
}
}
const isSplashDead = (splash: Splash): boolean => splash.age >= splash.maxAge;
const drawRaindrop = (ctx: CanvasRenderingContext2D, drop: Raindrop): void => {
ctx.globalAlpha = drop.opacity;
const xPos = drop.x * PIXEL_SIZE;
for (let i = 0; i < drop.length; i++) {
const colorIndex = Math.floor(
(i / drop.length) * NUM_COLOR_VARIATIONS * 0.5,
);
ctx.fillStyle = RAIN_COLORS[colorIndex];
ctx.fillRect(xPos, (drop.y - i) * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
}
ctx.globalAlpha = 1;
};
function drawSplash(ctx: CanvasRenderingContext2D, splash: Splash) {
if (splash.age >= splash.maxAge) return;
const progress = splash.age / splash.maxAge;
ctx.globalAlpha = 1 - progress;
ctx.fillStyle = SPLASH_PARTICLE_COLOR;
splash.particles.forEach((p) => {
const px = Math.floor(splash.x + p.offsetX) * PIXEL_SIZE;
const py = Math.floor(splash.y + p.offsetY) * PIXEL_SIZE;
ctx.fillRect(px, py, PIXEL_SIZE, PIXEL_SIZE);
});
if (splash.age < 6) {
const rippleSize = Math.floor(splash.age / 2) + 1;
const splashY = splash.y * PIXEL_SIZE;
ctx.fillStyle = SPLASH_RIPPLE_COLOR;
ctx.fillRect(
(splash.x - rippleSize) * PIXEL_SIZE,
splashY,
PIXEL_SIZE,
PIXEL_SIZE,
);
ctx.fillRect(
(splash.x + rippleSize) * PIXEL_SIZE,
splashY,
PIXEL_SIZE,
PIXEL_SIZE,
);
}
ctx.globalAlpha = 1;
}
function drawStar(
ctx: CanvasRenderingContext2D,
star: Star,
time: number,
) {
const twinkle = Math.sin(time * star.twinkleSpeed + star.twinkleOffset) *
STAR_TWINKLE_AMPLITUDE + STAR_TWINKLE_BASE;
const alpha = star.brightness * twinkle;
const x = Math.floor(star.x);
const y = Math.floor(star.y);
const color = ALPHA_STAR_COLORS[star.colorIndex];
ctx.fillStyle = color + alpha;
ctx.fillRect(x, y, star.size, star.size);
if (star.size === STAR_SIZE_LARGE && alpha > STAR_GLOW_THRESHOLD) {
ctx.fillStyle = color + (alpha * STAR_GLOW_ALPHA);
ctx.fillRect(x - 4, y + 2, 4, 4);
ctx.fillRect(x + 8, y + 2, 4, 4);
ctx.fillRect(x + 2, y - 4, 4, 4);
ctx.fillRect(x + 2, y + 8, 4, 4);
}
}
export default function Rain() {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const prefersReducedMotion =
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (prefersReducedMotion) return;
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
let gridWidth = 0;
let gridHeight = 0;
const resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gridWidth = Math.floor(window.innerWidth / PIXEL_SIZE);
gridHeight = Math.floor(window.innerHeight / PIXEL_SIZE);
};
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
const stars = Array.from({ length: STAR_COUNT }, createStar);
const raindrops = Array.from(
{ length: DROP_COUNT },
() => createRaindrop(gridWidth, gridHeight),
);
const splashes: Splash[] = [];
const startTime = performance.now();
const animate = (time: number) => {
ctx.fillStyle = BACKGROUND_COLOUR;
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < stars.length; i++) {
updateStar(stars[i]);
drawStar(ctx, stars[i], time - startTime);
}
raindrops.forEach((drop) =>
updateRaindrop(drop, splashes, gridWidth, gridHeight)
);
for (let i = splashes.length - 1; i >= 0; i--) {
updateSplash(splashes[i]);
if (isSplashDead(splashes[i])) {
splashes.splice(i, 1);
}
}
for (let i = 0; i < stars.length; i++) {
updateStar(stars[i]);
drawStar(ctx, stars[i], performance.now() - startTime);
}
raindrops.forEach((drop) => drawRaindrop(ctx, drop));
splashes.forEach((splash) => drawSplash(ctx, splash));
requestAnimationFrame(animate);
};
animate(0);
}, []);
return (
<canvas
ref={canvasRef}
style={{
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
imageRendering: "pixelated",
background: BACKGROUND_COLOUR,
}}
/>
);
}

49
islands/Time.tsx Normal file
View file

@ -0,0 +1,49 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { useEffect, useState } from "preact/hooks";
import Box from "../components/Box.tsx";
export default function Time() {
const formatter = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "numeric",
hour12: true,
timeZone: "America/Sao_Paulo",
});
const now = new Date();
const time = formatter.format(now);
const [offset, setOffset] = useState("");
useEffect(() => {
const br = new Date(
now.toLocaleString("en-US", {
timeZone: "America/Sao_Paulo",
}),
);
const local = new Date(now.toLocaleString("en-US"));
const diff = br.getTime() - local.getTime();
const ms = Math.abs(diff);
const hours = ~~(ms / 36e5);
const minutes = ~~((ms / 6e4) % 60);
let output = " · ";
if (hours) output += `You're ${hours} hour${hours > 1 ? "s" : ""} `;
if (hours && minutes) output += "and ";
if (minutes) output += `${minutes} minute${minutes > 1 ? "s" : ""} `;
if (hours || minutes) output += diff > 0 ? "behind" : "ahead";
else output = " · Hey, we're in the same time zone!";
setOffset(output);
}, []);
return (
<Box>
Brasília Time, {time}
{offset}
</Box>
);
}

12
main.ts Normal file
View file

@ -0,0 +1,12 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { App, staticFiles } from "fresh";
import { type State } from "./utils.ts";
export const app = new App<State>();
app.use(staticFiles());
app.fsRoutes();

31
routes/_app.tsx Normal file
View file

@ -0,0 +1,31 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { define } from "../utils.ts";
export default define.page(function App({ Component }) {
return (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/favicon.png" />
<meta property="og:site_name" content="acpi.at" />
<meta property="og:type" content="profile" />
<meta
property="og:description"
content="one of the girls of all time"
/>
<meta property="og:image" content="https://acpi.at/bnuy.webp" />
<meta property="og:title" content="mux's winterhouse" />
<meta name="theme-color" content="#a0bdfc" />
<title>mux's winterhouse</title>
</head>
<body>
<Component />
</body>
</html>
);
});

308
routes/index.tsx Normal file
View file

@ -0,0 +1,308 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { define } from "../utils.ts";
import Time from "../islands/Time.tsx";
import Rain from "../islands/Rain.tsx";
import { Centered, Section } from "../components/Layout.tsx";
import Title from "../components/Title.tsx";
import TintedImage from "../components/Tinted.tsx";
import Link from "../components/Link.tsx";
import Header from "../components/Header.tsx";
import Footer from "../components/Footer.tsx";
import Box from "../components/Box.tsx";
import Meow from "../islands/Meow.tsx";
import Name from "../islands/Name.tsx";
import Code from "../islands/Code.tsx";
import {
AttachmentIcon,
BlueskyIcon,
CodebergIcon,
DiscordIcon,
ForgejoIcon,
GitHubIcon,
LabelIcon,
LastfmIcon,
MailIcon,
SignalIcon,
TwitterIcon,
CommentIcon,
HeartIcon,
KofiIcon,
BitcoinIcon,
BitcoinCashIcon,
MoneroIcon,
NanoIcon,
LitecoinIcon,
EthereumIcon,
} from "../components/Icon.tsx";
export default define.page(() => {
return (
<>
<Rain />
<Centered>
<Header />
<Section>
<Title>
<HeartIcon class="icon" />
About me
</Title>
<p class="intro">
<span>
{" "}
muxiepuff{" "}
<span class="alt alt-font">(/ˈmuˌksipʌf/, she/her)</span>
{" · "}
</span>
I'm just your average advocate for open access to information and
knowledge and aspiring electrical engineer with complementary
interests in systems programming and linguistics. Neurodivergent
student passionate about libre software.
</p>
<p class="intro">
On the side, I maintain a modest FreeBSD server where I self-host
this website and various services. This is nothing particularly
impressive, just a growing curiosity about systems administration.
Service status and uptime available{" "}
<Link href="https://glance.acpi.at">here</Link>.
</p>
<Time />
</Section>
<Section>
<Title>
<LabelIcon class="icon" />
Links
</Title>
<div>
<Title level={3}>Social platforms</Title>
<ul class="labeled-icons">
<li>
<Box as="a" href="https://bsky.app/profile/acpi.at">
<BlueskyIcon /> @acpi.at
</Box>
</li>
<li>
<Box as="a" href="https://last.fm/user/auwora">
<LastfmIcon /> @auwora
</Box>
</li>
</ul>
</div>
<div>
<Title level={3}>Software and code</Title>
<ul class="labeled-icons">
<li>
<Box as="a" href="https://git.acpi.at/mux">
<ForgejoIcon /> mux
</Box>
</li>
<li>
<Box as="a" href="https://codeberg.org/ex">
<CodebergIcon /> ex
</Box>
</li>
<li>
<Box as="a" href="https://github.com/xwra">
<GitHubIcon /> xwra
</Box>
</li>
</ul>
</div>
<div>
<Title level={3}>Congenial folks</Title>
<ul class="labeled-icons b88x31">
<li>
<Meow />
</li>
<li>
<a href="https://worf.win">
<img
src="https://worf.win/images/worfwin.gif"
title="worf"
alt="worf"
/>
</a>
</li>
<li>
<a href="https://codeberg.org/paige">
<img src="/88x31/paige.gif" title="paige" alt="paige" />
</a>
</li>
<li>
<a href="https://mugman.tech">
<img
src="https://mugman.tech/88x31/me.gif"
title="mugman"
alt="mugman"
/>
</a>
</li>
<li>
<a href="https://f.dog">
<img
src="https://x86.pet/~strawberry/june_88x31.png"
title="june"
alt="june"
/>
</a>
</li>
<li>
<a
rel="noopener"
referrerpolicy="strict-origin"
href="https://rushii.dev"
>
<img
src="https://rushii.dev/88x31/rushii.webp"
title="rushii's site"
alt="rushii's site"
/>
</a>
</li>
<li>
<a href="https://www.juwuba.xyz">
<img
src="https://www.juwuba.xyz/88x31.gif"
title="Júlia"
alt="Júlia"
/>
</a>
</li>
<li>
<a href="https://katelyn.moe/">
<img
src="https://katelyn.moe/8831.png"
title="katelyn"
alt="katelyn"
/>
</a>
</li>
<li>
<a href="https://meow-d.github.io">
<img src="/88x31/meow_d.webp" title="meow_d" alt="meow_d" />
</a>
</li>
<li>
<a href="https://caitlyn.moe">
<img
src="https://caitlyn.moe/88x31.png"
title="caitlyn"
alt="caitlyn"
/>
</a>
</li>
</ul>
</div>
</Section>
<Section>
<Title>
<CommentIcon class="icon" />
Contact
</Title>
<p class="intro">
Feel free to reach out through any of the platforms listed above.
For email correspondence, you can reach me at{" "}
<Code>base64 -d &lt;&lt;&lt; bXV4QGFjcGkuYXQK</Code>.
</p>
<p class="intro">
Psst! When discussing sensitive matters over email or other insecure
communication channels, I'd really appreciate it if you could
encrypt your message with my <Link href="/pgp-key.asc">PGP key</Link>{" "}
(fingerprint:{" "}
<Code>AC14 9A39 5013 C572 CA74 8799 BCD2 117C 99E6 9817</Code>).
</p>
<div>
<Title level={3}>Communication</Title>
<ul class="labeled-icons">
<li>
<Box as="a" href="https://discord.com/users/797566974024351745">
<DiscordIcon /> @muxiepuff
</Box>
</li>
<li>
<Box
as="a"
href="https://signal.me/#eu/gdveRFng4iFhFkB4m5Esr2ciQ5FWZTeSFrCGa2osQ7ZrSu2d48RCdqgDc2nEzWQq"
>
<SignalIcon /> @mux.01
</Box>
</li>
</ul>
</div>
<hr />
<p class="intro">
If you enjoy throwing money at people on the internet, please
consider me! It keeps the server alive, fuels my tinkering with
esoteric technology, and helps me navigate some rough financial
patches and stay afloat while things are tight.
</p>
<div>
<Title level={3}>Donations</Title>
<ul class="labeled-icons full">
<li>
<Box as="a" href="https://ko-fi.com/west">
<KofiIcon /> west
</Box>
</li>
<li>
<Box as="span">
<MoneroIcon />{" "}
456tmp4CU158sV95Pjh75QfMcANNsRCFTdpmc86G31eKg9urvcYunnuBVyUmazAbuihUXwRDVojkhFTbzg6X2GuAJ7t3MCT
</Box>
</li>
<li>
<Box as="span">
<LitecoinIcon /> ltc1ql30jtr2r0pkr0wshspf465rdupvlpmwacql7wp
</Box>
</li>
<li>
<Box as="span">
<NanoIcon />{" "}nano_1qbbwrzxnutw53kuykdo6zcwteawf3befpbf95zrz66b33yxpz5ans6wrw4w
</Box>
</li>
<li>
<Box as="span">
<BitcoinIcon />{" "} bc1qj3l6cpxz604vlfz5zqlnhzr7g0aq7xlslnmzcm
</Box>
</li>
<li>
<Box as="span">
<BitcoinCashIcon />{" "}bitcoincash:qrs8y70dwzvz2kvxnv30c9k4pxa0vatzvudle5aaa3
</Box>
</li>
<li>
<Box as="span">
<EthereumIcon />{" "}0x9bb671035Dde19C674769592AbC20E63f36b5Aa9
</Box>
</li>
</ul>
</div>
</Section>
<Section>
<Title>
<AttachmentIcon class="icon" />
Extra
</Title>
<ul id="extra">
<li>
<TintedImage
src="/extra/mumengo.gif"
colour="var(--theme-accent-title)"
/>
</li>
<li>
<TintedImage
src="/extra/nenezil.gif"
colour="var(--theme-accent-title)"
/>
</li>
</ul>
</Section>
<Footer />
</Centered>
</>
);
});

31
scripts/copyright.ts Executable file
View file

@ -0,0 +1,31 @@
#!/usr/bin/env -S deno run --allow-read --allow-write
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { walk } from "https://deno.land/std/fs/walk.ts";
const copyrightHeader = `/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
`;
const dir = "./";
for await (
const entry of walk(dir, {
exts: [".ts", ".tsx"],
includeDirs: false,
skip: [/node_modules/, /copyright\.ts$/],
})
) {
const filePath = entry.path;
const content = await Deno.readTextFile(filePath);
if (!content.startsWith(copyrightHeader)) {
await Deno.writeTextFile(filePath, copyrightHeader + "\n" + content);
console.log(`Added header to ${filePath}`);
}
}

BIN
static/88x31.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
static/88x31.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
static/88x31/meow_d.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
static/88x31/paige.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,021 B

BIN
static/88x31@1000.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
static/88x31@1000.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
static/bnuy.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
static/extra/mumengo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

BIN
static/extra/nenezil.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
static/fonts/jersey15.ttf Normal file

Binary file not shown.

BIN
static/fonts/m6x11.ttf Normal file

Binary file not shown.

BIN
static/fonts/m6x11plus.ttf Normal file

Binary file not shown.

BIN
static/fonts/str.ttf Normal file

Binary file not shown.

BIN
static/icons/smiley.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

243
static/logo.svg Normal file
View file

@ -0,0 +1,243 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg
version="1.1"
width="380"
height="380"
xmlns="http://www.w3.org/2000/svg"
shape-rendering="crispEdges"
>
<rect x="80" y="70" width="30" height="10" fill="#1E2545" />
<rect x="270" y="70" width="30" height="10" fill="#1E2545" />
<rect x="60" y="80" width="20" height="10" fill="#1E2545" />
<rect x="80" y="80" width="10" height="10" fill="#5E76AB" />
<rect x="90" y="80" width="10" height="20" fill="#A0BDFC" />
<rect x="100" y="80" width="10" height="10" fill="#5E76AB" />
<rect x="110" y="80" width="20" height="10" fill="#1E2545" />
<rect x="250" y="80" width="20" height="10" fill="#1E2545" />
<rect x="270" y="80" width="10" height="10" fill="#5E76AB" />
<rect x="280" y="80" width="10" height="20" fill="#A0BDFC" />
<rect x="290" y="80" width="10" height="10" fill="#5E76AB" />
<rect x="300" y="80" width="20" height="10" fill="#1E2545" />
<rect x="50" y="90" width="10" height="10" fill="#1E2545" />
<rect x="60" y="90" width="10" height="10" fill="#5E76AB" />
<rect x="70" y="90" width="20" height="10" fill="#A0BDFC" />
<rect x="100" y="90" width="20" height="10" fill="#A0BDFC" />
<rect x="120" y="90" width="10" height="10" fill="#5E76AB" />
<rect x="130" y="90" width="10" height="10" fill="#1E2545" />
<rect x="240" y="90" width="10" height="10" fill="#1E2545" />
<rect x="250" y="90" width="10" height="10" fill="#5E76AB" />
<rect x="260" y="90" width="20" height="10" fill="#A0BDFC" />
<rect x="290" y="90" width="20" height="10" fill="#A0BDFC" />
<rect x="310" y="90" width="10" height="10" fill="#5E76AB" />
<rect x="320" y="90" width="10" height="10" fill="#1E2545" />
<rect x="40" y="100" width="10" height="10" fill="#1E2545" />
<rect x="50" y="100" width="10" height="10" fill="#5E76AB" />
<rect x="60" y="100" width="40" height="60" fill="#DFF2FE" />
<rect x="100" y="100" width="10" height="10" fill="#FAFEFF" />
<rect x="110" y="100" width="30" height="10" fill="#A0BDFC" />
<rect x="140" y="100" width="10" height="10" fill="#1E2545" />
<rect x="230" y="100" width="10" height="10" fill="#1E2545" />
<rect x="240" y="100" width="30" height="10" fill="#A0BDFC" />
<rect x="270" y="100" width="10" height="10" fill="#FAFEFF" />
<rect x="280" y="100" width="40" height="60" fill="#DFF2FE" />
<rect x="320" y="100" width="10" height="10" fill="#5E76AB" />
<rect x="330" y="100" width="10" height="10" fill="#1E2545" />
<rect x="30" y="110" width="10" height="10" fill="#1E2545" />
<rect x="40" y="110" width="10" height="10" fill="#A0BDFC" />
<rect x="50" y="110" width="10" height="70" fill="#DFF2FE" />
<rect x="100" y="110" width="10" height="50" fill="#DFF2FE" />
<rect x="110" y="110" width="10" height="10" fill="#FAFEFF" />
<rect x="120" y="110" width="10" height="10" fill="#C0D4FF" />
<rect x="130" y="110" width="20" height="10" fill="#A0BDFC" />
<rect x="150" y="110" width="10" height="10" fill="#1E2545" />
<rect x="220" y="110" width="10" height="10" fill="#1E2545" />
<rect x="230" y="110" width="20" height="10" fill="#A0BDFC" />
<rect x="250" y="110" width="10" height="10" fill="#C0D4FF" />
<rect x="260" y="110" width="10" height="10" fill="#FAFEFF" />
<rect x="270" y="110" width="10" height="50" fill="#DFF2FE" />
<rect x="320" y="110" width="10" height="70" fill="#DFF2FE" />
<rect x="330" y="110" width="10" height="10" fill="#A0BDFC" />
<rect x="340" y="110" width="10" height="10" fill="#1E2545" />
<rect x="20" y="120" width="10" height="10" fill="#1E2545" />
<rect x="30" y="120" width="10" height="10" fill="#A0BDFC" />
<rect x="40" y="120" width="10" height="60" fill="#DFF2FE" />
<rect x="110" y="120" width="20" height="50" fill="#DFF2FE" />
<rect x="130" y="120" width="10" height="10" fill="#FAFEFF" />
<rect x="140" y="120" width="10" height="10" fill="#A0BDFC" />
<rect x="150" y="120" width="10" height="10" fill="#5E76AB" />
<rect x="160" y="120" width="10" height="10" fill="#1E2545" />
<rect x="210" y="120" width="10" height="10" fill="#1E2545" />
<rect x="220" y="120" width="10" height="10" fill="#5E76AB" />
<rect x="230" y="120" width="10" height="10" fill="#A0BDFC" />
<rect x="240" y="120" width="10" height="10" fill="#FAFEFF" />
<rect x="250" y="120" width="20" height="50" fill="#DFF2FE" />
<rect x="330" y="120" width="10" height="60" fill="#DFF2FE" />
<rect x="340" y="120" width="10" height="10" fill="#A0BDFC" />
<rect x="350" y="120" width="10" height="10" fill="#1E2545" />
<rect x="10" y="130" width="10" height="10" fill="#1E2545" />
<rect x="20" y="130" width="10" height="50" fill="#A0BDFC" />
<rect x="30" y="130" width="10" height="40" fill="#DFF2FE" />
<rect x="130" y="130" width="10" height="30" fill="#DFF2FE" />
<rect x="140" y="130" width="10" height="30" fill="#FAFEFF" />
<rect x="150" y="130" width="20" height="60" fill="#A0BDFC" />
<rect x="170" y="130" width="10" height="10" fill="#1E2545" />
<rect x="200" y="130" width="10" height="10" fill="#1E2545" />
<rect x="210" y="130" width="20" height="60" fill="#A0BDFC" />
<rect x="230" y="130" width="10" height="30" fill="#FAFEFF" />
<rect x="240" y="130" width="10" height="30" fill="#DFF2FE" />
<rect x="340" y="130" width="10" height="40" fill="#DFF2FE" />
<rect x="350" y="130" width="10" height="50" fill="#A0BDFC" />
<rect x="360" y="130" width="10" height="10" fill="#1E2545" />
<rect x="0" y="140" width="10" height="30" fill="#1E2545" />
<rect x="10" y="140" width="10" height="10" fill="#5E76AB" />
<rect x="170" y="140" width="10" height="10" fill="#5E76AB" />
<rect x="180" y="140" width="20" height="10" fill="#1E2545" />
<rect x="200" y="140" width="10" height="10" fill="#5E76AB" />
<rect x="360" y="140" width="10" height="10" fill="#5E76AB" />
<rect x="370" y="140" width="10" height="30" fill="#1E2545" />
<rect x="10" y="150" width="10" height="10" fill="#A0BDFC" />
<rect x="170" y="150" width="10" height="30" fill="#A0BDFC" />
<rect x="180" y="150" width="20" height="10" fill="#5E76AB" />
<rect x="200" y="150" width="10" height="30" fill="#A0BDFC" />
<rect x="360" y="150" width="10" height="10" fill="#A0BDFC" />
<rect x="10" y="160" width="10" height="10" fill="#5E76AB" />
<rect x="60" y="160" width="10" height="10" fill="#DFF2FE" />
<rect x="70" y="160" width="10" height="10" fill="#FAFEFF" />
<rect x="80" y="160" width="10" height="10" fill="#C0D4FF" />
<rect x="90" y="160" width="20" height="10" fill="#A0BDFC" />
<rect x="130" y="160" width="10" height="10" fill="#FAFEFF" />
<rect x="140" y="160" width="10" height="50" fill="#A0BDFC" />
<rect x="180" y="160" width="20" height="10" fill="#A0BDFC" />
<rect x="230" y="160" width="10" height="50" fill="#A0BDFC" />
<rect x="240" y="160" width="10" height="10" fill="#FAFEFF" />
<rect x="270" y="160" width="20" height="10" fill="#A0BDFC" />
<rect x="290" y="160" width="10" height="10" fill="#C0D4FF" />
<rect x="300" y="160" width="10" height="10" fill="#FAFEFF" />
<rect x="310" y="160" width="10" height="10" fill="#DFF2FE" />
<rect x="360" y="160" width="10" height="10" fill="#5E76AB" />
<rect x="10" y="170" width="10" height="10" fill="#1E2545" />
<rect x="30" y="170" width="10" height="20" fill="#A0BDFC" />
<rect x="60" y="170" width="10" height="10" fill="#FAFEFF" />
<rect x="70" y="170" width="20" height="10" fill="#A0BDFC" />
<rect x="90" y="170" width="10" height="10" fill="#1E2545" />
<rect x="100" y="170" width="40" height="10" fill="#A0BDFC" />
<rect x="180" y="170" width="20" height="70" fill="#DFF2FE" />
<rect x="240" y="170" width="40" height="10" fill="#A0BDFC" />
<rect x="280" y="170" width="10" height="10" fill="#1E2545" />
<rect x="290" y="170" width="20" height="10" fill="#A0BDFC" />
<rect x="310" y="170" width="10" height="10" fill="#FAFEFF" />
<rect x="340" y="170" width="10" height="20" fill="#A0BDFC" />
<rect x="360" y="170" width="10" height="10" fill="#1E2545" />
<rect x="20" y="180" width="10" height="10" fill="#1E2545" />
<rect x="40" y="180" width="10" height="10" fill="#A0BDFC" />
<rect x="50" y="180" width="10" height="10" fill="#FAFEFF" />
<rect x="60" y="180" width="20" height="10" fill="#A0BDFC" />
<rect x="80" y="180" width="10" height="10" fill="#1E2545" />
<rect x="100" y="180" width="10" height="10" fill="#1E2545" />
<rect x="110" y="180" width="30" height="30" fill="#A0BDFC" />
<rect x="170" y="180" width="10" height="60" fill="#DFF2FE" />
<rect x="200" y="180" width="10" height="60" fill="#DFF2FE" />
<rect x="240" y="180" width="30" height="30" fill="#A0BDFC" />
<rect x="270" y="180" width="10" height="10" fill="#1E2545" />
<rect x="290" y="180" width="10" height="10" fill="#1E2545" />
<rect x="300" y="180" width="20" height="10" fill="#A0BDFC" />
<rect x="320" y="180" width="10" height="10" fill="#FAFEFF" />
<rect x="330" y="180" width="10" height="10" fill="#A0BDFC" />
<rect x="350" y="180" width="10" height="10" fill="#1E2545" />
<rect x="30" y="190" width="10" height="10" fill="#1E2545" />
<rect x="40" y="190" width="10" height="10" fill="#5E76AB" />
<rect x="50" y="190" width="20" height="10" fill="#A0BDFC" />
<rect x="70" y="190" width="10" height="10" fill="#1E2545" />
<rect x="90" y="190" width="10" height="20" fill="#1E2545" />
<rect x="100" y="190" width="10" height="10" fill="#5E76AB" />
<rect x="150" y="190" width="10" height="10" fill="#A0BDFC" />
<rect x="160" y="190" width="10" height="10" fill="#C0D4FF" />
<rect x="210" y="190" width="10" height="10" fill="#C0D4FF" />
<rect x="220" y="190" width="10" height="10" fill="#A0BDFC" />
<rect x="270" y="190" width="10" height="10" fill="#5E76AB" />
<rect x="280" y="190" width="10" height="20" fill="#1E2545" />
<rect x="300" y="190" width="10" height="10" fill="#1E2545" />
<rect x="310" y="190" width="20" height="10" fill="#A0BDFC" />
<rect x="330" y="190" width="10" height="10" fill="#5E76AB" />
<rect x="340" y="190" width="10" height="10" fill="#1E2545" />
<rect x="40" y="200" width="30" height="10" fill="#1E2545" />
<rect x="100" y="200" width="10" height="60" fill="#A0BDFC" />
<rect x="150" y="200" width="10" height="10" fill="#C0D4FF" />
<rect x="160" y="200" width="10" height="120" fill="#DFF2FE" />
<rect x="210" y="200" width="10" height="120" fill="#DFF2FE" />
<rect x="220" y="200" width="10" height="10" fill="#C0D4FF" />
<rect x="270" y="200" width="10" height="60" fill="#A0BDFC" />
<rect x="310" y="200" width="30" height="10" fill="#1E2545" />
<rect x="80" y="210" width="10" height="20" fill="#1E2545" />
<rect x="90" y="210" width="10" height="10" fill="#5E76AB" />
<rect x="110" y="210" width="20" height="30" fill="#A0BDFC" />
<rect x="130" y="210" width="20" height="10" fill="#333C64" />
<rect x="150" y="210" width="10" height="110" fill="#DFF2FE" />
<rect x="220" y="210" width="10" height="110" fill="#DFF2FE" />
<rect x="230" y="210" width="20" height="10" fill="#333C64" />
<rect x="250" y="210" width="20" height="30" fill="#A0BDFC" />
<rect x="280" y="210" width="10" height="10" fill="#5E76AB" />
<rect x="290" y="210" width="10" height="20" fill="#1E2545" />
<rect x="90" y="220" width="10" height="50" fill="#A0BDFC" />
<rect x="130" y="220" width="20" height="30" fill="#1E2545" />
<rect x="230" y="220" width="20" height="30" fill="#1E2545" />
<rect x="280" y="220" width="10" height="50" fill="#A0BDFC" />
<rect x="70" y="230" width="10" height="20" fill="#1E2545" />
<rect x="80" y="230" width="10" height="10" fill="#5E76AB" />
<rect x="290" y="230" width="10" height="10" fill="#5E76AB" />
<rect x="300" y="230" width="10" height="20" fill="#1E2545" />
<rect x="80" y="240" width="10" height="80" fill="#A0BDFC" />
<rect x="110" y="240" width="10" height="10" fill="#A0BDFC" />
<rect x="120" y="240" width="10" height="10" fill="#C0D4FF" />
<rect x="170" y="240" width="40" height="10" fill="#FC9797" />
<rect x="250" y="240" width="10" height="10" fill="#C0D4FF" />
<rect x="260" y="240" width="10" height="10" fill="#A0BDFC" />
<rect x="290" y="240" width="10" height="80" fill="#A0BDFC" />
<rect x="60" y="250" width="10" height="20" fill="#1E2545" />
<rect x="70" y="250" width="10" height="10" fill="#5E76AB" />
<rect x="110" y="250" width="10" height="10" fill="#C0D4FF" />
<rect x="120" y="250" width="10" height="70" fill="#DFF2FE" />
<rect x="130" y="250" width="20" height="10" fill="#EBF5FF" />
<rect x="170" y="250" width="10" height="30" fill="#DFF2FE" />
<rect x="180" y="250" width="20" height="10" fill="#FC9797" />
<rect x="200" y="250" width="10" height="30" fill="#DFF2FE" />
<rect x="230" y="250" width="20" height="10" fill="#EBF5FF" />
<rect x="250" y="250" width="10" height="70" fill="#DFF2FE" />
<rect x="260" y="250" width="10" height="10" fill="#C0D4FF" />
<rect x="300" y="250" width="10" height="10" fill="#5E76AB" />
<rect x="310" y="250" width="10" height="20" fill="#1E2545" />
<rect x="70" y="260" width="10" height="60" fill="#A0BDFC" />
<rect x="100" y="260" width="10" height="10" fill="#C0D4FF" />
<rect x="110" y="260" width="10" height="60" fill="#DFF2FE" />
<rect x="130" y="260" width="20" height="60" fill="#DFF2FE" />
<rect x="180" y="260" width="20" height="10" fill="#DFF2FE" />
<rect x="230" y="260" width="20" height="60" fill="#DFF2FE" />
<rect x="260" y="260" width="10" height="60" fill="#DFF2FE" />
<rect x="270" y="260" width="10" height="10" fill="#C0D4FF" />
<rect x="300" y="260" width="10" height="60" fill="#A0BDFC" />
<rect x="50" y="270" width="10" height="40" fill="#1E2545" />
<rect x="60" y="270" width="10" height="10" fill="#5E76AB" />
<rect x="90" y="270" width="10" height="10" fill="#C0D4FF" />
<rect x="100" y="270" width="10" height="50" fill="#DFF2FE" />
<rect x="180" y="270" width="20" height="10" fill="#9EAEBA" />
<rect x="270" y="270" width="10" height="50" fill="#DFF2FE" />
<rect x="280" y="270" width="10" height="10" fill="#C0D4FF" />
<rect x="310" y="270" width="10" height="10" fill="#5E76AB" />
<rect x="320" y="270" width="10" height="40" fill="#1E2545" />
<rect x="60" y="280" width="10" height="20" fill="#A0BDFC" />
<rect x="90" y="280" width="10" height="30" fill="#DFF2FE" />
<rect x="170" y="280" width="10" height="10" fill="#B8CAD9" />
<rect x="180" y="280" width="20" height="40" fill="#DFF2FE" />
<rect x="200" y="280" width="10" height="10" fill="#B8CAD9" />
<rect x="280" y="280" width="10" height="30" fill="#DFF2FE" />
<rect x="310" y="280" width="10" height="20" fill="#A0BDFC" />
<rect x="170" y="290" width="10" height="30" fill="#DFF2FE" />
<rect x="200" y="290" width="10" height="30" fill="#DFF2FE" />
<rect x="60" y="300" width="10" height="10" fill="#5E76AB" />
<rect x="310" y="300" width="10" height="10" fill="#5E76AB" />
<rect x="60" y="310" width="10" height="10" fill="#1E2545" />
<rect x="90" y="310" width="10" height="10" fill="#C0D4FF" />
<rect x="280" y="310" width="10" height="10" fill="#C0D4FF" />
<rect x="310" y="310" width="10" height="10" fill="#1E2545" />
<rect x="70" y="320" width="240" height="10" fill="#1E2545" />
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

51
static/pgp-key.asc Normal file
View file

@ -0,0 +1,51 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=XHXh
-----END PGP PUBLIC KEY BLOCK-----

14
utils.ts Normal file
View file

@ -0,0 +1,14 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { createDefine } from "fresh";
// This specifies the type of "ctx.state" which is used to share
// data among middlewares, layouts and routes.
export interface State {
shared: string;
}
export const define = createDefine<State>();

11
vite.config.ts Normal file
View file

@ -0,0 +1,11 @@
/**
* Copyright (c) 2025 xwra
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { defineConfig } from "vite";
import { fresh } from "@fresh/plugin-vite";
export default defineConfig({
plugins: [fresh()],
});