send help

This commit is contained in:
Seth 2025-05-17 16:08:59 -04:00
parent 8c000ba0b7
commit b76e879350
104 changed files with 4260 additions and 142 deletions

View file

@ -1,61 +0,0 @@
.scanlines {
overflow: hidden;
}
.scanlines:before,
.scanlines:after {
display: inherit;
pointer-events: none;
content: "";
position: absolute;
}
.scanlines:before {
width: 100%;
height: 2px;
z-index: 2147483649;
background: rgba(0, 0, 0, 0.3);
opacity: 0.75;
animation: scanline 6s linear infinite;
}
.scanlines:after {
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 2147483648;
background: linear-gradient(
to bottom,
transparent 50%,
rgba(0, 0, 0, 0.3) 51%
);
background-size: 100% 6px;
animation: scanlines 2s steps(30) infinite;
}
/* ANIMATE UNIQUE SCANLINE */
@keyframes scanline {
0% {
transform: translate3d(0, 200000%, 0);
}
}
@keyframes scanlines {
0% {
background-position: 0 50%;
}
}
span.shj-syn-str:nth-child(2) {
color: var(--status-color, rgba(150, 150, 150, 0.1));
}
.shj-numbers {
padding: 0px;
}
.shj-lang-json {
padding: 0px;
background-color: transparent;
}

View file

@ -1,27 +1,40 @@
import "mdui/components/layout";
import "mdui/components/layout-main";
import Hyperate from "./components/Hyperate";
import Lanyard from "./components/Lanyard";
import NavigationBar from "./components/NavigationBar";
import TopAppBar from "./components/TopAppBar";
export default () => {
return (
<div class="app terminal">
<p>[seth@ipv4 ~]$ cat ./about.txt</p>
<p>
A Dedicated Backend Developer,
<br />
with a passion for high-fidelity audio,
<br />
gaming, and web development.
</p>
<div>
<TopAppBar />
<mdui-layout>
<mdui-layout-main>
<div class="app terminal">
<p>[seth@ipv4 ~]$ cat ./about.txt</p>
<p>
A Dedicated Backend Developer,
<br />
with a passion for high-fidelity audio,
<br />
gaming, and web development.
</p>
<p>[seth@ipv4 ~]$ cat /tmp/discord-ipc</p>
<p>
<Lanyard />
</p>
<p>[seth@ipv4 ~]$ cat /tmp/discord-ipc</p>
<p>
<Lanyard />
</p>
<p>[seth@ipv4 ~]$ cat /tmp/heartrate</p>
<p>
<Hyperate />
</p>
<p>[seth@ipv4 ~]$ cat /tmp/heartrate</p>
<p>
<Hyperate />
</p>
</div>
</mdui-layout-main>
</mdui-layout>
<NavigationBar />
</div>
);
};

View file

@ -1,6 +1,6 @@
import { createRef } from "tsx-dom";
import socket from "../../Socket";
import socket from "../../utilities/socket";
export default () => {
const paragraph = createRef<HTMLParagraphElement>();

View file

@ -1,20 +1,6 @@
import { highlightElement } from "@speed-highlight/core";
import { createRef } from "tsx-dom";
import socket from "../../Socket";
const statusTypes = {
online: "0, 150, 0",
idle: "150, 150, 0",
dnd: "150, 0, 0",
offline: "150, 150, 150",
};
const gradientTypes = {
online: "0, 150, 0",
idle: "150, 150, 0",
dnd: "150, 0, 0",
offline: "150, 150, 150",
};
import socket from "../../utilities/socket";
const activityTypes: Record<number, string> = {
0: "Playing",
@ -31,8 +17,6 @@ export default () => {
socket.addEventListener("lanyard", (event: Event) => {
const lanyard = (event as CustomEvent<LanyardData>).detail;
document.body.style = `--status-color: rgb(${statusTypes[lanyard.discord_status]}); --gradient-color: rgba(${gradientTypes[lanyard.discord_status]}, 0.1);`;
if (container.current) {
container.current.textContent = JSON.stringify(
{

View file

@ -0,0 +1,29 @@
import "mdui/components/navigation-bar";
import "mdui/components/navigation-bar-item";
import "mdui/components/button-icon";
import "@mdui/icons/person--outlined";
import "@mdui/icons/person--rounded";
import "@mdui/icons/more-vert--rounded";
export default () => {
return (
<mdui-navigation-bar value="item-1">
<mdui-navigation-bar-item value="item-1">
<mdui-icon-person--outlined slot="icon" />
<mdui-icon-person--rounded slot="active-icon" />
Item 1
</mdui-navigation-bar-item>
<mdui-navigation-bar-item value="item-2">
<mdui-icon-more-vert--rounded slot="icon" /> Item 2
</mdui-navigation-bar-item>
<mdui-navigation-bar-item value="item-3">
<mdui-icon-more-vert--rounded slot="icon" /> Item 3
</mdui-navigation-bar-item>
<mdui-navigation-bar-item value="item-4">
<mdui-icon-more-vert--rounded slot="icon" /> Item 4
</mdui-navigation-bar-item>
</mdui-navigation-bar>
);
};

View file

@ -0,0 +1,11 @@
import "mdui/components/top-app-bar";
import "mdui/components/top-app-bar-title";
import "mdui/components/button-icon";
export default () => {
return (
<mdui-top-app-bar variant="center-aligned" scroll-behavior="elevate">
<mdui-top-app-bar-title>Seth @ IPv4.ARMY</mdui-top-app-bar-title>
</mdui-top-app-bar>
);
};

View file

@ -1,44 +1,167 @@
@import "../../node_modules/@speed-highlight/core/dist/themes/dark.css";
@import "./App.css";
@import "../../node_modules/@fontsource/roboto/latin-400.css";
@import "../../node_modules/@fontsource/roboto-mono/latin-400.css";
@import "../../node_modules/mdui/mdui.css";
html,
head,
body {
margin: 0;
padding: 0;
font: 2vh monospace;
height: 100vh;
width: 100vw;
font-family: "Roboto", sans-serif;
}
body {
color: #dedede;
text-shadow: 0 0 5px #c8c8c8;
background: radial-gradient(
at bottom right,
var(--gradient-color, rgba(150, 150, 150, 0.1)) 0%,
rgba(0, 0, 0, 1) 100%
);
display: flex;
height: 100vh;
width: 100vw;
overflow: hidden;
}
p {
margin: 0;
padding: 0;
line-height: 1.4em;
}
.terminal {
white-space: pre-wrap;
font-family: monospace;
width: 100vw;
height: 100vh;
overflow-y: auto;
display: flex;
flex-direction: column;
[class*="shj-lang-"] {
white-space: pre;
color: #112;
text-shadow: none;
box-sizing: border-box;
gap: 0.4em;
background: #dedede;
border-radius: 10px;
max-width: min(100%, 100vw);
margin: 10px 0;
padding: 30px 20px;
font:
18px / 24px "Roboto Mono",
monospace;
box-shadow: 0 0 5px #0001;
}
.shj-inline {
border-radius: 5px;
margin: 0;
padding: 2px 5px;
display: inline-block;
}
[class*="shj-lang-"]::selection {
background: #bdf5;
}
[class*="shj-lang-"] ::selection {
background: #bdf5;
}
[class*="shj-lang-"] > div {
display: flex;
overflow: auto;
}
[class*="shj-lang-"] > div :last-child {
outline: none;
flex: 1;
}
.shj-numbers {
counter-reset: line;
padding-left: 5px;
}
.shj-numbers div {
padding-right: 5px;
}
.shj-numbers div:before {
color: #999;
content: counter(line);
opacity: 0.5;
text-align: right;
counter-increment: line;
margin-right: 5px;
display: block;
}
.shj-syn-cmnt {
font-style: italic;
}
.shj-syn-err,
.shj-syn-kwd {
color: #e16;
}
.shj-syn-num,
.shj-syn-class {
color: #f60;
}
.shj-syn-insert,
.shj-syn-str {
color: #7d8;
}
.shj-syn-bool {
color: #3bf;
}
.shj-syn-type,
.shj-syn-oper {
color: #5af;
}
.shj-syn-section,
.shj-syn-func {
color: #84f;
}
.shj-syn-deleted,
.shj-syn-var {
color: #f44;
}
.shj-oneline {
padding: 12px 10px;
}
.shj-lang-http.shj-oneline .shj-syn-kwd {
color: #fff;
background: #25f;
border-radius: 5px;
padding: 5px 7px;
}
[class*="shj-lang-"] {
color: #f8f8f2;
background: var(--mdui-color-secondary-container);
}
[class*="shj-lang-"]:before {
color: #6f9aff;
}
.shj-syn-deleted,
.shj-syn-err,
.shj-syn-var {
color: #ff5261;
}
.shj-syn-section,
.shj-syn-kwd {
color: #ff7cc6;
}
.shj-syn-class {
color: #eab07c;
}
.shj-numbers,
.shj-syn-cmnt {
color: #7d828b;
}
.shj-syn-insert,
.shj-syn-type,
.shj-syn-func,
.shj-syn-bool {
color: #71d58a;
}
.shj-syn-num {
color: #b581fd;
}
.shj-syn-oper {
color: #80c6ff;
}
.shj-syn-str {
color: #4dacfa;
}

View file

@ -1,9 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="mdui-theme-dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no" />
<meta name="renderer" content="webkit" />
<meta name="theme-color" content="#000000">
<meta name="description"
content="A Dedicated Backend Developer, with a passion for high-fidelity audio, gaming, and web development.">
@ -15,8 +16,8 @@
<link rel="stylesheet" href="index.css" />
</head>
<body class="scanlines">
<script src="index.tsx"></script>
<body>
<script src="index.tsx" type="module"></script>
</body>
</html>

View file

@ -1,5 +1,7 @@
import "tsx-dom";
import "./utilities/clicker";
import App from "./App";
document.body.appendChild(<App />);

View file

@ -0,0 +1,21 @@
const effectTick = new Audio("https://no.ipv4.army/u/Effect_Tick.ogg");
effectTick.volume = 0.1;
const whitelistedTags = ["mdui-button", "mdui-icon"];
document.onclick = (event: MouseEvent) => {
const target = event.target as HTMLElement;
if (!target) return;
const tagName = target.tagName.toLowerCase();
const isWhitelisted = whitelistedTags.some((tag) => tagName.startsWith(tag));
console.log(tagName, isWhitelisted);
if (!isWhitelisted) return;
effectTick.currentTime = 0;
effectTick.play();
};

View file

@ -0,0 +1,108 @@
import type { Snackbar } from "mdui";
import { snackbar } from "mdui";
interface Options {
/**
* The text to display in the snackbar.
*/
message: string;
/**
* The position of the snackbar. Defaults to `bottom`. Possible values are:
* * `top`: Aligned to the top, centered
* * `top-start`: Aligned to the top, left
* * `top-end`: Aligned to the top, right
* * `bottom`: Aligned to the bottom, centered
* * `bottom-start`: Aligned to the bottom, left
* * `bottom-end`: Aligned to the bottom, right
*/
placement?:
| "top"
| "top-start"
| "top-end"
| "bottom"
| "bottom-start"
| "bottom-end";
/**
* The text for the action button.
*/
action?: string;
/**
* Whether to show a close button on the right.
*/
closeable?: boolean;
/**
* The maximum number of lines to display for the message text. Defaults to no limit. Possible values are:
* * `1`: The message text will be displayed on one line at most.
* * `2`: The message text will be displayed on two lines at most.
*/
messageLine?: 1 | 2;
/**
* The duration (in milliseconds) after which the snackbar will automatically close. If set to 0, it will not close automatically. Defaults to 5 seconds.
*/
autoCloseDelay?: number;
/**
* Whether to close the snackbar when clicking or touching outside of it.
*/
closeOnOutsideClick?: boolean;
/**
* The name of the queue.
* By default, queues are not enabled. When calling this function multiple times, multiple snackbars will be displayed simultaneously.
* You can pass a queue name as an argument. Snackbar functions with the same queue name will only open after the previous snackbar has closed.
*/
queue?: string;
/**
* The callback function to be executed when the snackbar is clicked.
* The function parameter is the snackbar instance, and `this` also points to the snackbar instance.
* @param snackbar
*/
onClick?: (snackbar: Snackbar) => void;
/**
* The callback function to be executed when the action button is clicked.
* The function parameter is the snackbar instance, and `this` also points to the snackbar instance.
* By default, clicking the action button will close the snackbar. If the return value is false, the snackbar will not close. If the return value is a promise, the snackbar will close after the promise is resolved.
* @param snackbar
*/
onActionClick?: (snackbar: Snackbar) => void | boolean | Promise<void>;
/**
* The callback function to be executed when the snackbar is opened.
* The function parameter is the snackbar instance, and `this` also points to the snackbar instance.
* @param snackbar
*/
onOpen?: (snackbar: Snackbar) => void;
/**
* The callback function to be executed when the snackbar's show animation is complete.
* The function parameter is the snackbar instance, and `this` also points to the snackbar instance.
* @param snackbar
*/
onOpened?: (snackbar: Snackbar) => void;
/**
* The callback function to be executed when the snackbar is about to be closed.
* The function parameter is the snackbar instance, and `this` also points to the snackbar instance.
* @param snackbar
*/
onClose?: (snackbar: Snackbar) => void;
/**
* The callback function to be executed when the snackbar's hide animation is complete.
* The function parameter is the snackbar instance, and `this` also points to the snackbar instance.
* @param snackbar
*/
onClosed?: (snackbar: Snackbar) => void;
}
const popcorn = new Audio("https://no.ipv4.army/u/Popcorn.ogg");
popcorn.volume = 0.1;
export const snacker = (opts: Options) => {
snackbar({
closeable: true,
messageLine: 2,
queue: "snacker",
onOpen: () => {
popcorn.currentTime = 0;
popcorn.play();
},
...opts,
});
};

View file

@ -8,6 +8,9 @@ class Socket extends EventTarget {
this._socket = new WebSocket(url);
this._socket.onmessage = (event) => {
if (event.data === "ping") return;
if (event.data === "pong") return;
const { type, data } = JSON.parse(event.data);
switch (type) {
@ -20,7 +23,7 @@ class Socket extends EventTarget {
break;
}
case "echo": {
console.log("Echo: ", data);
//console.log("Echo: ", data);
break;
}
default: {
@ -36,7 +39,7 @@ class Socket extends EventTarget {
setInterval(() => {
this._socket.send("ping");
}, 30 * 1000);
}, 10 * 1000);
}
emitLanyard(lanyard: LanyardData) {

View file

@ -30,10 +30,12 @@ const server = serve({
"/assets/:file": async (req) =>
Backend.Responses.file(file(`./dist/${req.params.file}`)),
"/public/*": async (req) => {
return Backend.Responses.file(file(`.${new URL(req.url).pathname}`));
},
"/robots.txt": async () =>
Backend.Responses.file(file("./public/robots.txt")),
"/favicon.svg": async () =>
Backend.Responses.file(file("./public/favicon.svg")),
"/api/server": () => {
const safeProcess = JSON.parse(JSON.stringify(process));
@ -71,7 +73,7 @@ const server = serve({
},
websocket: {
idleTimeout: 1,
idleTimeout: 60,
open: (ws) => {
ws.subscribe("lanyard");
ws.send(JSON.stringify({ type: "lanyard", data: lanyard }), true);
@ -83,7 +85,9 @@ const server = serve({
);
},
message: (ws, msg) => {
ws.send(JSON.stringify({ type: "echo", data: msg }), true);
if (msg === "ping") ws.send("pong", true);
if (msg === "pong") ws.send("ping", true);
return;
},
},
});