website 2
All checks were successful
/ test (push) Successful in 14s

This commit is contained in:
Soph :3 2024-07-23 01:04:20 +03:00
parent a5793e087c
commit 013c7cef7f
Signed by: sophie
GPG key ID: EDA5D222A0C270F2
27 changed files with 626 additions and 358 deletions

View file

@ -11,7 +11,7 @@ jobs:
apt-get -qq install rsync
shell: bash
- run: bun install
- run: bun src/build.ts --build
- run: bun run prod
- name: Install SSH Key
uses: http://github.com/shimataro/ssh-key-action@v2
with:

3
.gitignore vendored
View file

@ -173,6 +173,3 @@ dist
# Finder (MacOS) folder config
.DS_Store
dist
src/web/dist.js

10
LICENSE
View file

@ -1,10 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View file

@ -1,4 +1,7 @@
# sophie's website
this website is very simple it is located in src/web.
there is a very sophisticated build script in src/build.ts !
it even supports HMR!
# sophie's website 2
contains a extremely complicated buildscript
run prod with `bun run prod`
edit ssg plugins in src/plugins
read src/buildsystem.md for more info

BIN
bun.lockb

Binary file not shown.

View file

@ -1,24 +1,23 @@
{
"name": "sophie",
"module": "index.ts",
"name": "website2",
"module": "src/index.ts",
"type": "module",
"scripts": {
"prod": "bun src/index.ts --prod",
"dev": "bun src/index.ts --dev"
},
"devDependencies": {
"@types/bun": "latest",
"@types/node": "^20.11.30",
"@types/p5": "^1.7.6",
"@types/serve-handler": "^6.1.4",
"esbuild": "0.20.2",
"micro": "^10.0.1",
"serve-handler": "^6.1.5"
"@types/bun": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"scripts": {
"dev": "bun src/build.ts --dev",
"build": "bun src/build.ts --build"
},
"dependencies": {
"ws": "^8.16.0"
"@types/mime-types": "^2.1.4",
"@types/p5": "^1.7.6",
"esbuild": "^0.23.0",
"marked": "^13.0.2",
"mime-types": "^2.1.35",
"p5": "^1.9.4"
}
}
}

View file

@ -1,99 +0,0 @@
import * as esbuild from "esbuild";
import { watch, cpSync, rmdirSync, readFileSync, ReadStream } from "node:fs";
import { serve } from "micro";
import { Server } from "node:http";
import handler from "serve-handler";
import { Readable } from "node:stream";
import { WebSocketServer } from "ws";
const script = `let t;function rr() {console.log("connecting to dev server")
let a = new WebSocket("ws://localhost:8081");
a.addEventListener("message", g => {
if(g.data == "refresh"){
location.reload()
}
});
a.addEventListener("open", () => {
console.log("connected")
})
a.addEventListener("close", () => {
console.log("socket closed, restarting in 1s")
clearInterval(t)
t = setTimeout(()=>{
rr()
},1000)
})};window.addEventListener("load", () => rr())`;
const argv = process.argv.slice(2);
async function buildTs() {
console.log("[dev] Building file..");
await esbuild.build({
entryPoints: ["./src/web/ts/index.ts"],
outfile: "./src/web/dist.js",
});
}
await buildTs();
if (argv[0] == "--build") {
try {
rmdirSync("./dist");
} catch {}
cpSync("./src/web/", "./dist/", { recursive: true });
rmdirSync("./dist/ts", { recursive: true });
console.log("[dev] View the dist folder");
}
if (argv[0] == "--dev") {
const wss = new WebSocketServer({ port: 8081 });
let allConnections = new Map<number, WebSocket>();
wss.on("connection", function connection(ws) {
const id = Math.random();
allConnections.set(id, ws as any);
ws.on("error", console.error);
ws.on("close", () => {
allConnections.delete(id);
});
});
const server = new Server(
serve(async (req, res) => {
await handler(
req,
res,
{
directoryListing: false,
public: "src/web/",
cleanUrls: false
},
{
createReadStream(path, options) {
let sx = readFileSync(path).toString("utf8");
if (!path.toString().endsWith(".html")) {
return Readable.from([sx]) as ReadStream;
}
sx = sx.replace("<head>", `<head><script>${script}</script>`);
return Readable.from([sx]) as ReadStream;
},
}
);
})
);
server.listen(8080);
console.log("[http] Listening HTTP on 8080.");
watch(
"./src/web",
{
recursive: true,
},
async (e, f) => {
if (f == "dist.js") return;
console.log("[dev] Noticed update in " + f + ", of type " + e + ".");
allConnections.forEach((z) => z.send("refresh"));
await buildTs();
}
);
}

111
src/index.ts Normal file
View file

@ -0,0 +1,111 @@
/*
*/
// Five at nit of fredyer
// Hor hor
export abstract class Plugin {
abstract name: string;
abstract rewriteTriggers: string[];
renameTo?: string;
abstract longLasting: boolean;
abstract rewriteFile(
file: string,
filePath: string
): Promise<string | undefined | null | void>;
}
import * as fs from "fs";
import * as path from "path";
if (!fs.existsSync("dist")) fs.mkdirSync("dist");
const plugins: Plugin[] = [];
for await (const file of fs.readdirSync(path.join("src", "plugins"))) {
const clas = (await import(path.join(__dirname, "plugins", file))).default;
const plug = new clas();
plugins.push(plug);
}
export async function build() {
try {
fs.rmdirSync("dist");
} catch {}
const sourceFiles = fs.readdirSync(path.join("website"), {
recursive: true,
withFileTypes: true,
});
const globalPlugins = plugins.filter((z) => z.rewriteTriggers.includes("*"));
for await (const file of sourceFiles) {
if (!file.isFile()) continue;
const type = file.name.split(".").at(-1);
if (!type) continue;
const shortname = file.name.slice(0, file.name.length - (type.length + 1));
const availablePlugins = plugins.filter((z) =>
z.rewriteTriggers.includes(type)
);
if (availablePlugins.length == 0) {
const oldPath = path.join(file.parentPath, file.name);
fs.cpSync(oldPath, oldPath.replace("website", "dist"));
}
for await (const plugin of availablePlugins) {
const oldPath = path.join(file.parentPath, file.name);
const newPath = path
.join(
file.parentPath,
shortname +
"." +
(plugin.rewriteTriggers.includes("*") ? type : plugin.renameTo)
)
.replace("website", "dist");
let data = fs.readFileSync(oldPath).toString("utf8");
for await (const globalPlugin of globalPlugins) {
const rewritten = await globalPlugin.rewriteFile(data, oldPath);
if (!rewritten) continue;
data = rewritten;
}
let rewrite = await plugin.rewriteFile(data, oldPath);
if (!rewrite) continue;
fs.mkdirSync(path.dirname(newPath), { recursive: true });
fs.writeFileSync(newPath, rewrite);
}
}
}
await build();

93
src/plugins/dev.ts Normal file
View file

@ -0,0 +1,93 @@
import type { Server, ServerWebSocket } from "bun";
import { Plugin, build } from "..";
import * as fs from "fs";
import mime from "mime-types";
const script = `let reconnectTimeout;function connect(){console.log("[--dev] connecting to dev server");let ws=new WebSocket("ws://localhost:8080");ws.addEventListener("message",message=>{if(message.data=="refresh"){location.reload()}});ws.addEventListener("open",()=>{console.log("[--dev] connected")});ws.addEventListener("close",()=>{console.log("[--dev] socket closed, restarting in 1s");clearTimeout(reconnectTimeout);reconnectTimeout=setTimeout(()=>{connect()},1e3)})}window.addEventListener("load",()=>connect());`;
export default class DevPlugin extends Plugin {
name = "dev";
rewriteTriggers = [];
renameTo = undefined;
longLasting = true;
server!: Server;
allConnections: ServerWebSocket<number>[] = [];
constructor() {
super();
if (!process.argv.includes("--dev")) return;
fs.watch(
"./website",
{
recursive: true,
},
async (e, f) => {
console.log("[dev] Noticed update in " + f + ", of type " + e + ".");
this.allConnections.forEach((z) => z.send("refresh"));
await build();
}
);
this.server = Bun.serve<number>({
fetch(req, server) {
const success = server.upgrade(req);
if (success) {
return undefined;
}
const url = new URL(req.url);
let cleanedPath = url.pathname;
if (cleanedPath == "/") cleanedPath = "/index.html";
if (cleanedPath.endsWith("/")) cleanedPath = cleanedPath.slice(0, -1);
let fsPath = "./dist" + cleanedPath;
if (fsPath.match(/\.\.\//g) !== null) {
return undefined;
}
let rawFile;
try {
rawFile = fs.readFileSync(fsPath);
} catch {
return new Response("404 Not Found", {
status: 404,
});
}
const type = fsPath.split(".").at(-1);
if (!type) return;
if (type == "html") {
rawFile = rawFile.toString().replace(
"<head>",
`<head><script>${script}</script>`
);
}
return new Response(rawFile, {
headers: {
"Content-Type": (mime.lookup(type) || "application/octet-stream") + "; charset=utf-8",
},
});
},
websocket: {
open: (ws) => {
ws.data = Math.random();
this.allConnections.push(ws);
},
message(ws, message) {},
close: (ws) => {
this.allConnections = this.allConnections.filter(
(z) => z.data != ws.data
);
},
},
port: 8080,
});
}
async rewriteFile(file: string, filePath: string) {
return undefined;
}
}

View file

@ -0,0 +1,22 @@
import { Plugin } from "..";
import { marked } from "marked";
import { parseMetadata } from "./markdown-metadata";
export default class MarkdownCompiler extends Plugin {
name = "markdown-compiler";
rewriteTriggers = ["md"]
renameTo = "html"
longLasting = false;
async rewriteFile(file: string, filePath: string) {
let text = file;
const metadata = parseMetadata(text);
if(metadata) {
let textSplit = text.split('\n');
textSplit.splice(0, Object.keys(metadata).length);
textSplit.unshift(metadata.title)
text = textSplit.join("\n");
}
return await marked.parse(text);
}
}

View file

@ -0,0 +1,29 @@
import { Plugin } from "..";
export function parseMetadata(file: string) {
if (!/^=+$/gm.test(file)) return;
const splitfile = file.split("\n");
let properties: Record<string, string> | undefined;
for (let i = 0; i < splitfile.length; i++) {
if (!properties) properties = {};
const line = splitfile[i];
if (/^=+$/gm.test(line)) break;
const parts = line.split("=");
if (parts.length !== 2) break;
properties[parts[0].trim()] = parts[1].trim();
}
return properties;
}
export default class MarkdownMetadataGenerator extends Plugin {
name = "markdown-metadata";
rewriteTriggers = ["md"];
renameTo = "json";
longLasting = false;
async rewriteFile(file: string, filePath: string) {
const metadata = parseMetadata(file);
if (!metadata) return;
return JSON.stringify(metadata);
}
}

View file

@ -0,0 +1,41 @@
import { Plugin } from "..";
import * as fs from "fs";
import * as esbuild from "esbuild";
export default class TSCompiler extends Plugin {
name = "ts-compiler";
rewriteTriggers = ["ts", "tsx", "jsx"];
renameTo = "js";
longLasting = false;
minify = false;
constructor() {
super();
if(process.argv.includes("--prod")) {
this.minify = true;
}
}
async rewriteFile(file: string, filePath: string) {
const result = await esbuild.build({
stdin: {
contents: file,
sourcefile: filePath.split("/").at(-1),
loader: "ts"
},
write: false,
outdir: 'out',
minify: this.minify
});
if(result.errors.length != 0) {
console.log("TS compiler errored.")
result.errors.forEach(element => {
console.error(element);
});
} else {
const output = result.outputFiles[0].contents;
return (new TextDecoder()).decode(output);
}
}
}

36
src/plugins/variables.ts Normal file
View file

@ -0,0 +1,36 @@
import { Plugin } from "..";
import * as fs from "fs";
import * as path from "path";
export default class Variables extends Plugin {
name = "variables";
rewriteTriggers = ["html", "*"];
renameTo = undefined;
longLasting = false;
variables: Record<string, string> = {};
constructor() {
super();
this.variables["__BLOG_POSTS__"] = JSON.stringify(
fs.readdirSync("./website/blogs")
);
const templatePath = path.resolve(__dirname, "../../website/templates");
if (fs.existsSync(templatePath)) {
for (const file of fs.readdirSync(templatePath)) {
const id = file.toUpperCase().replace(".HTML", "");
this.variables["__TEMPLATE_" + id + "__"] = fs
.readFileSync(path.join(templatePath, file))
.toString("utf8");
}
}
}
async rewriteFile(file: string, filePath: string): Promise<string> {
let prevfile = file;
for (const a of Object.entries(this.variables)) {
prevfile = prevfile.replaceAll(a[0], a[1]);
}
return prevfile;
}
}

97
src/readme.md Normal file
View file

@ -0,0 +1,97 @@
# sad.ovh build system
design goals:
1. rewrite and generate custom HTML/TS/JS/whatever..
2. allow for variables and other important features in buildsystem->html
3. plugins, this ties together the top two
4. HMR and HTML/CSS reloading (in the Dev plugin)
5. Rewriteable file renaming
6. Every plugin runs on the same set of files, and can rename and reinject multiple times
Psudeo-code build.ts
```ts
class Plugin {
.. whatever's needed to support the plugin system ..
}
if(.. not exist dist ..)
.. make dist ..
const plugins: Plugin[] = [];
.. dynamically load plugins into plugins ..
const srcFiles: File[] = []
.. get files into file ..
export function build () {
for(const file of srcFiles) {
const plugins = plugins.filter(z=>z.rewriteTriggers(file.type));
if(plugins.length == 0) continue;
for (const plugin of plugins) {
let filename = file.name + "." + file.type;
let newFilename = path.join("dist", file.name + "." + plugin.renameTo || file.type);
fs.writeFileSync(newFilename, plugin.rewriteFile(fs.readFileSync(filename), filename))
}
}
}
build();
```
Psudeo-code plugins
```ts
class DevPlugin extends Plugin {
rewriteTriggers: ["html"]
renameTo: undefined
longLasting: false;
.. websocket server ..
constructor(.. options from argv of the main executable..) {
.. ran on start of main index.ts executable ..
.. this would be long lasting, so leave if not using --dev ..
.. use a method on the server that rebuilds if required ..
.. Would run a web server too ..
}
rewriteFile(file: string, filePath: string) {
return file.replace("<head>", `<head><script>${script}</script>`)
}
}
```
```ts
class TSCompiler extends Plugin {
rewriteTriggers: ["ts"]
renameTo: "js"
rewriteFile(file: string, filePath: string) {
// use SWC or TS or esbuild, whatever
}
}
```
```ts
class MarkdownMetadataGenerator extends Plugin {
rewriteTriggers: ["md"]
renameTo: "json"
rewriteFile(file: string, filePath: string) {
return marked.parse(file);
}
}
```
```ts
class MarkdownCompiler extends Plugin {
rewriteTriggers: ["md"]
renameTo: "html"
rewriteFile(file: string, filePath: string) {
return marked.parse(file);
}
}
```

View file

@ -1,106 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<!-- Primary Meta Tags -->
<title>soph's blog and information</title>
<meta name="title" content="soph's blog and information" />
<meta name="description" content="yourfriend.lol v2, soph's blog and information"" />
<!-- Open Graph / Facebook -->
<meta property=" og:type" content="website" />
<meta property="og:url" content="https://sad.ovh" />
<meta property="og:title" content="soph's blog and information" />
<meta property="og:description" content="yourfriend.lol v2, soph's blog and information" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://sad.ovh" />
<meta property="twitter:title" content="soph's blog and information" />
<meta property="twitter:description" content="yourfriend.lol v2, soph's blog and information"" />
<meta name=" viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kimeiga/bahunya/dist/bahunya.min.css">
</head>
<body>
<h1> sophie's blog </h1>
<a id="return_back" href="/blog.html" style="display:none;font-size:xx-large;">return back?</a>
<div>Scroll to bottom for comments ↓</div>
<ul id="html_list" style="display: none">
</ul>
<div id="renderer"></div>
<div style="color:red;font-size:larger;font-weight:bolder;display:none;" id="error">
Blog post <span></span> does not exist.
</div>
<div class="giscus"></div>
<script src="https://giscus.app/client.js" data-repo="fucksophie/blog_comments" data-repo-id="R_kgDOMY4cfw"
data-category="General" data-category-id="DIC_kwDOMY4cf84ChCRR" data-mapping="url" data-strict="1"
data-reactions-enabled="1" data-emit-metadata="0" data-input-position="bottom" data-theme="noborder_dark"
data-lang="en" data-loading="lazy" crossorigin="anonymous" async>
</script>
<script type="module">
function parseMetadata(file) {
const splitfile = file.split("\n");
const properties = {};
for (let i = 0; i < splitfile.length; i++) {
const line = splitfile[i];
if (/^=+$/gm.test(line)) break;
const parts = line.split("=");
if (parts.length !== 2) break;
properties[parts[0].trim()] = parts[1].trim();
}
return properties;
}
const uriParams = new URLSearchParams(location.search);
if (uriParams.has("md")) {
const error = document.getElementById("error");
const renderer = document.getElementById('renderer');
const return_back = document.getElementById("return_back");
const req = await fetch("/blogs/" + uriParams.get("md"));
const giscus = document.querySelector(".giscus");
if (req.status != 200) {
error.style.display = "block";
giscus.style.display = "none";
} else {
let text = await req.text();
const metadata = parseMetadata(text);
text = text.split('\n');
text.splice(0, Object.keys(metadata).length);
text.unshift(metadata.title)
text = text.join("\n");
renderer.innerHTML = marked.parse(text)
}
return_back.style.display = "block";
} else {
const blog_posts = ["opensource-watch-comparison.md", "raspberry-pi-struggles.md", "why-i-syncthing.md"];
const html_list = document.getElementById("html_list");
html_list.style.display = "block";
for (const blog_post of blog_posts) {
const req = await fetch("/blogs/" + blog_post);
const blog_post_contents = await req.text();
let metadata;
try {
metadata = parseMetadata(blog_post_contents);
} catch {
console.error("ran into issue loading metadata for blog post " + blog_post)
continue;
}
const li = document.createElement("li");
const a = document.createElement("a");
a.href = "/blog.html?md=" + encodeURIComponent(blog_post);
a.innerText = `${metadata.title} (created ${new Date(metadata.time * 1000).toGMTString()})`;
li.appendChild(a);
html_list.appendChild(li);
}
}
</script>
</body>
</html>

View file

@ -1,119 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.2/p5.min.js"
integrity="sha512-eu9vkh+EbAsW3fMmPTj/DP5W3UegIdu0Z/OABMocvoofx43MYBkcQ9hRIVxZndV1vcCYQwBg+U1PkWl04TD0Jg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<!-- Primary Meta Tags -->
<title>sophie's personal site</title>
<meta name="title" content="sophie's personal site" />
<meta name="description" content="Wow! :3 Meowww.. Nyaaa! sad.ovh, yourfriend.lol v2. Has a blog too . (maybe)
Nyaaaaaa Kitty website :3" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://sad.ovh" />
<meta property="og:title" content="sophie's personal site" />
<meta property="og:description" content="Wow! :3 Meowww.. Nyaaa! sad.ovh, yourfriend.lol v2. Has a blog too . (maybe)
Nyaaaaaa Kitty website :3" />
<meta property="og:image" content="https://sad.ovh/Screenshot_2023-02-04_16-45-32.png" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://sad.ovh" />
<meta property="twitter:title" content="sophie's personal site" />
<meta property="twitter:description" content="Wow! :3 Meowww.. Nyaaa! sad.ovh, yourfriend.lol v2. Has a blog too . (maybe)
Nyaaaaaa Kitty website :3" />
<meta property="twitter:image" content="https://sad.ovh/Screenshot_2023-02-04_16-45-32.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="dist.js"></script>
<style>
body[data-theme="light"] {
color: #31363F;
}
body[data-theme="dark"] {
color: #eeeeee;
}
body[data-theme="dark"]>.center>a {
color: #eeeeee !important;
}
body[data-theme="light"]>.center>a {
color: #31363F !important;
}
:root {
font-family: Inter, sans-serif;
font-feature-settings: 'liga' 1, 'calt' 1;
}
@supports (font-variation-settings: normal) {
:root {
font-family: InterVariable, sans-serif;
}
}
.center {
position: absolute;
top: 10px;
left: 10px;
}
.selected {
font-weight: bold;
}
</style>
</head>
<body>
<span class="center">
<div>
<button class="selected" id="home-button">home</button> | <button id="donations-button">donations</button> |
<a href="/blog.html"><button>blog</button></a>
</div>
<span id="home">
I'm Latvian, 17. My name's Sophie. I love listening to music, I have <span
id="songplays">&lt;loading...&gt;</span> song plays.
<br>
I am a JS/TS developer, mostly specializing in backend work.
<br>
I play minecraft, and upgun with friends. I have developed many bukkit plugins for Paper/Folia/Spigot
before, and am pretty well versed in them.
<br>
Contact me at <a href="https://matrix.to/#/@yourfriend:bark.lgbt">matrix</a>, <a
href="https://discord.com/users/845374523263811614">discord</a>, <a
href="https://github.com/fucksophie">github (view projects here)</a>, <a
href="https://git.sad.ovh/sophie">sadgit</a>, or <a href="https://bark.lgbt/@yourfriend">mastodon</a>
<br>
<a href="/key.txt">Here's my PGP key.</a>
</span>
<span id="donations" style="display:none">
<ul>
<li>BTC bc1q83jdukjn4a2qm0rmn9tqcfkcq60la22lqy2shx</li>
<li>ETH/BSC/<strong>USDT</strong>/USDC (send via BSC) 0xc691cd8950Fdf96Faa2aCA1CA9b4B3Fd5B2a44BB</li>
<li>SOL 79NKoiXaPzbwbsD5MFKKwmoeEPKtTsoQFfx64MHmULF7</li>
<li><strong>XMR
42iW3icQrybKYieQNSrm76dXetuXD6HaxZDijajXkge7GTSKVG4NefxBj3mbWudpY62dxRTihm4beJgy36X8xFKCTWpVAjS</strong>
</li>
</ul>
OR <a href="https://ko-fi.com/sophskofi">kofi</a> OR, dm for paypal
<br>
<img src="/sticker.webp" alt="monero-chan" style="width:128px;">
</span>
</span>
<span style="position:absolute;left:10px;bottom:10px">
Click <kbd>spacebar</kbd> to change the themes.
</span>
</body>
</html>

24
website/blog.html Normal file
View file

@ -0,0 +1,24 @@
__TEMPLATE_HEAD__
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/kimeiga/bahunya/dist/bahunya.min.css">
</head>
<body>
<h1> sophie's blog </h1>
<a id="return_back" href="/blog.html" style="display:none;font-size:xx-large;">return back?</a>
<div>Scroll to bottom for comments ↓</div>
<ul id="html_list" style="display: none">
</ul>
<div id="renderer"></div>
<div style="color:red;font-size:larger;font-weight:bolder;display:none;" id="error">
Blog post <span></span> does not exist.
</div>
<div class="giscus"></div>
<script src="https://giscus.app/client.js" data-repo="fucksophie/blog_comments" data-repo-id="R_kgDOMY4cfw"
data-category="General" data-category-id="DIC_kwDOMY4cf84ChCRR" data-mapping="url" data-strict="1"
data-reactions-enabled="1" data-emit-metadata="0" data-input-position="bottom" data-theme="noborder_dark"
data-lang="en" data-loading="lazy" crossorigin="anonymous" async>
</script>
<script type="module" src="blog.js"></script>
</body>
</html>

34
website/blog.ts Normal file
View file

@ -0,0 +1,34 @@
const uriParams = new URLSearchParams(location.search);
if (uriParams.has("md")) {
const error = document.getElementById("error")!;
const renderer = document.getElementById("renderer")!;
const return_back = document.getElementById("return_back")!;
const req = await fetch("/blogs/" + uriParams.get("md") + ".html");
const giscus = document.querySelector(".giscus")! as HTMLDivElement;
if (req.status != 200) {
error.style.display = "block";
giscus.style.display = "none";
} else {
let text = await req.text();
renderer.innerHTML = text;
}
return_back.style.display = "block";
} else {
//@ts-expect-error
const blog_posts = __BLOG_POSTS__.map(z => z.replace(".md", ""))
const html_list = document.getElementById("html_list")!;
html_list.style.display = "block";
for (const blog_post of blog_posts) {
const req = await fetch("/blogs/" + blog_post + ".json");
const metadata = await req.json();
const li = document.createElement("li");
const a = document.createElement("a");
a.href = "/blog.html?md=" + encodeURIComponent(blog_post);
a.innerText = `${metadata.title} (created ${new Date(
metadata.time * 1000
//@ts-expect-error
).toGMTString()})`;
li.appendChild(a);
html_list.appendChild(li);
}
}

54
website/index.html Normal file
View file

@ -0,0 +1,54 @@
__TEMPLATE_HEAD__
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.2/p5.min.js"
integrity="sha512-eu9vkh+EbAsW3fMmPTj/DP5W3UegIdu0Z/OABMocvoofx43MYBkcQ9hRIVxZndV1vcCYQwBg+U1PkWl04TD0Jg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="/style.css">
<script src="index.js"></script>
</head>
<body>
<span class="center">
<div>
<button class="selected" id="home-button">home</button> | <button id="donations-button">donations</button> |
<a href="/blog.html"><button>blog</button></a>
</div>
<span id="home">
I'm Latvian, 17. My name's Sophie. I love listening to music, I have <span
id="songplays">&lt;loading...&gt;</span> song plays.
<br>
I am a JS/TS developer, mostly specializing in backend work.
<br>
I play minecraft, and upgun with friends. I have developed many bukkit plugins for Paper/Folia/Spigot
before, and am pretty well versed in them.
<br>
Contact me at <a href="https://matrix.to/#/@yourfriend:bark.lgbt">matrix</a>, <a
href="https://discord.com/users/845374523263811614">discord</a>, <a
href="https://github.com/fucksophie">github (view projects here)</a>, <a
href="https://git.sad.ovh/sophie">sadgit</a>, or <a href="https://bark.lgbt/@yourfriend">mastodon</a>
<br>
<a href="/key.txt">Here's my PGP key.</a>
</span>
<span id="donations" style="display:none">
<ul>
<li>BTC bc1q83jdukjn4a2qm0rmn9tqcfkcq60la22lqy2shx</li>
<li>ETH/BSC/<strong>USDT</strong>/USDC (send via BSC) 0xc691cd8950Fdf96Faa2aCA1CA9b4B3Fd5B2a44BB</li>
<li>SOL 79NKoiXaPzbwbsD5MFKKwmoeEPKtTsoQFfx64MHmULF7</li>
<li><strong>XMR
42iW3icQrybKYieQNSrm76dXetuXD6HaxZDijajXkge7GTSKVG4NefxBj3mbWudpY62dxRTihm4beJgy36X8xFKCTWpVAjS</strong>
</li>
</ul>
OR <a href="https://ko-fi.com/sophskofi">kofi</a> OR, dm for paypal
<br>
<img src="/sticker.webp" alt="monero-chan" style="width:128px;">
</span>
</span>
<span style="position:absolute;left:10px;bottom:10px">
Click <kbd>spacebar</kbd> to change the themes.
</span>
</body>
</html>

View file

@ -378,4 +378,4 @@ function mouseDragged() {
}
}
}
}
}

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

36
website/style.css Normal file
View file

@ -0,0 +1,36 @@
body[data-theme="light"] {
color: #31363F;
}
body[data-theme="dark"] {
color: #eeeeee;
}
body[data-theme="dark"]>.center>a {
color: #eeeeee !important;
}
body[data-theme="light"]>.center>a {
color: #31363F !important;
}
:root {
font-family: Inter, sans-serif;
font-feature-settings: 'liga' 1, 'calt' 1;
}
@supports (font-variation-settings: normal) {
:root {
font-family: InterVariable, sans-serif;
}
}
.center {
position: absolute;
top: 10px;
left: 10px;
}
.selected {
font-weight: bold;
}

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<link rel="preconnect" href="https://rsms.me/">
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
<!-- Primary Meta Tags -->
<title>soph's blog and information</title>
<meta name="title" content="soph's blog and information" />
<meta name="description" content="yourfriend.lol v2, soph's blog and information"" />
<!-- Open Graph / Facebook -->
<meta property=" og:type" content="website" />
<meta property="og:url" content="https://sad.ovh" />
<meta property="og:title" content="soph's blog and information" />
<meta property="og:description" content="yourfriend.lol v2, soph's blog and information" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://sad.ovh" />
<meta property="twitter:title" content="soph's blog and information" />
<meta property="twitter:description" content="yourfriend.lol v2, soph's blog and information"" />
<meta name=" viewport" content="width=device-width, initial-scale=1.0">