parent
a5793e087c
commit
013c7cef7f
|
@ -11,7 +11,7 @@ jobs:
|
||||||
apt-get -qq install rsync
|
apt-get -qq install rsync
|
||||||
shell: bash
|
shell: bash
|
||||||
- run: bun install
|
- run: bun install
|
||||||
- run: bun src/build.ts --build
|
- run: bun run prod
|
||||||
- name: Install SSH Key
|
- name: Install SSH Key
|
||||||
uses: http://github.com/shimataro/ssh-key-action@v2
|
uses: http://github.com/shimataro/ssh-key-action@v2
|
||||||
with:
|
with:
|
||||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -173,6 +173,3 @@ dist
|
||||||
|
|
||||||
# Finder (MacOS) folder config
|
# Finder (MacOS) folder config
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
dist
|
|
||||||
src/web/dist.js
|
|
10
LICENSE
10
LICENSE
|
@ -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/>
|
|
11
README.md
11
README.md
|
@ -1,4 +1,7 @@
|
||||||
# sophie's website
|
# sophie's website 2
|
||||||
this website is very simple it is located in src/web.
|
|
||||||
there is a very sophisticated build script in src/build.ts !
|
contains a extremely complicated buildscript
|
||||||
it even supports HMR!
|
|
||||||
|
run prod with `bun run prod`
|
||||||
|
edit ssg plugins in src/plugins
|
||||||
|
read src/buildsystem.md for more info
|
29
package.json
29
package.json
|
@ -1,24 +1,23 @@
|
||||||
{
|
{
|
||||||
"name": "sophie",
|
"name": "website2",
|
||||||
"module": "index.ts",
|
"module": "src/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"prod": "bun src/index.ts --prod",
|
||||||
|
"dev": "bun src/index.ts --dev"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@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"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"dev": "bun src/build.ts --dev",
|
|
||||||
"build": "bun src/build.ts --build"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
99
src/build.ts
99
src/build.ts
|
@ -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
111
src/index.ts
Normal 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
93
src/plugins/dev.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
22
src/plugins/markdown-compiler.ts
Normal file
22
src/plugins/markdown-compiler.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
29
src/plugins/markdown-metadata.ts
Normal file
29
src/plugins/markdown-metadata.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
41
src/plugins/ts-compiler.ts
Normal file
41
src/plugins/ts-compiler.ts
Normal 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
36
src/plugins/variables.ts
Normal 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
97
src/readme.md
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -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>
|
|
|
@ -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"><loading...></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
24
website/blog.html
Normal 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
34
website/blog.ts
Normal 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
54
website/index.html
Normal 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"><loading...></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>
|
|
@ -378,4 +378,4 @@ function mouseDragged() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
36
website/style.css
Normal file
36
website/style.css
Normal 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;
|
||||||
|
}
|
26
website/templates/head.html
Normal file
26
website/templates/head.html
Normal 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">
|
||||||
|
|
Loading…
Reference in a new issue