function _internal_joinPaths(path1, path2) {
if (path2[0] == "/") {
return _internal_sanitizePath(path2);
}
else if (path2[0] == "~" && (path2.length == 1 || path2[1] == "/")) {
return _internal_sanitizePath(HOME_DIR + path2.slice(1));
}
else {
return _internal_sanitizePath(path1 + "/" + path2);
}
}
function _internal_sanitizePath(path) {
while (path.includes("//")) {
path = path.replaceAll("//", "/");
}
while (path[path.length - 1] == "/") {
path = path.slice(0, path.length - 1);
}
let newPath = [];
for (const dir of path.split("/")) {
if (dir == ".") {
}
else if (dir == "..") {
if (newPath.length) {
newPath = newPath.slice(0, newPath.length - 1);
}
}
else if (dir) {
newPath.push(dir);
}
}
return "/" + newPath.join("/");
}
function _internal_getFile(path) {
function getFile_recursive(files, path) {
console.log(files, path);
let file = files[path[0]];
if (!file) {
return null;
}
if (path.length == 1) {
return file;
}
if (file.type == "file") {
return null;
}
return getFile_recursive(file.files, path.slice(1));
}
if (path == "/") {
return {
type: "directory",
name: "/",
files: FILESYSTEM
};
}
let dirs = path.split("/");
if (dirs[0] == "") {
dirs = dirs.slice(1);
}
return getFile_recursive(FILESYSTEM, dirs);
}
function _internal_stringifyPath(path) {
if (path.startsWith(HOME_DIR)) {
return path.replace(HOME_DIR, "~");
}
return path;
}
function _internal_getPS1(winInfo) {
return `${winInfo.ps1Override || "trinkey@website"}:${_internal_stringifyPath(winInfo.PWD)}$ `;
}
function _internal_getFlags(command) {
let flags = [];
let newCommand = [];
for (const thing of command.split(" ")) {
if (thing.startsWith("-")) {
flags.push(...thing.slice(1).split(""));
}
else {
newCommand.push(thing);
}
}
return {
flags: flags,
removed: newCommand.join(" ")
};
}
function _internal_fileSize(size) {
let suffix = "";
let amount = size;
const sizes = [
{ suffix: "K", amount: 1024, threshold: 10000 },
{ suffix: "M", amount: Math.pow(1024, 2), threshold: 1000000 }
];
for (const info of sizes) {
if (size > info.threshold) {
suffix = info.suffix;
amount = Math.round(size / info.amount * 10) / 10;
if (String(amount).length > 3) {
amount = Math.round(info.amount);
}
}
}
return `${amount}${suffix}`.padStart(4, " ");
}
function cat(path, windowID) {
if (!path) {
return "
cat: You must specify a file
";
}
path = _internal_joinPaths(windowInformation[windowID].PWD || HOME_DIR, path);
let file = _internal_getFile(path);
if (!file) {
return `cat: ${escapeHTML(path)}: No such file or directory
`;
}
else if (file.type == "directory") {
return `cat: ${escapeHTML(path)}: Is a directory
`;
}
else {
return file.content;
}
}
function cd(path, windowID) {
if (!path) {
windowInformation[windowID].PWD = HOME_DIR;
return "";
}
let newPWD = _internal_joinPaths(windowInformation[windowID].PWD, path);
let newFileObj = _internal_getFile(newPWD);
if (newFileObj === null) {
return `cd: ${escapeHTML(path)}: No such file or directory
`;
}
else if (newFileObj.type == "file") {
return `cd: ${escapeHTML(path)}: Not a directory
`;
}
windowInformation[windowID].PWD = newPWD;
return "";
}
function clear(path, windowID) {
WINDOWS[windowID].element.querySelector(".window").innerHTML = "";
return "";
}
function exit(path, windowID) {
setTimeout(() => {
WINDOWS[windowID].element.querySelector(".close").click();
}, 1);
return "";
}
function help(path, windowID) {
return helpText;
}
function ls(path, windowID) {
let { flags, removed } = _internal_getFlags(path);
path = _internal_joinPaths(windowInformation[windowID].PWD || HOME_DIR, removed);
let file = _internal_getFile(path);
let files;
if (!file) {
return `ls: ${escapeHTML(path)}: No such file or directory
`;
}
else if (file.type == "file") {
files = { [file.name]: file };
}
else {
files = file.files;
}
let directories = [];
let hidden = flags.includes("a") || flags.includes("A");
let long = flags.includes("l");
let out = flags.includes("a") ? (long ? "drwxrwxr-x trinkey trinkey 4096 .
drwxr-xr-x trinkey trinkey 4096 ..
" : ". .. ") : "";
for (const file of Object.keys(files).sort((a, b) => (({ true: 1, false: -1 })[String(flags.includes("r") ? a < b : a > b)]))) {
if (file.startsWith(".") && !hidden) {
continue;
}
let fObj = files[file];
if (long) {
if (fObj.type == "directory") {
directories.push(fObj);
out += `drwxrwxr-x trinkey trinkey 4096 ${escapeHTML(file)}
`;
}
else {
out += `-rw-rw-r-- trinkey trinkey ${_internal_fileSize(fObj.content.length).replaceAll(" ", " ")} ${escapeHTML(file)}
`;
}
}
else {
if (fObj.type == "directory") {
directories.push(fObj);
out += `${escapeHTML(file)} `;
}
else {
out += `${escapeHTML(file)} `;
}
}
}
if (!long) {
out = out.slice(0, out.length - 7);
}
if (flags.includes("R")) {
for (const dir of directories) {
out += `
${escapeHTML(_internal_stringifyPath(_internal_joinPaths(path, dir.name)))}:
${ls(`${_internal_joinPaths(path, dir.name)} -${flags.join("")}`, windowID)}`;
}
}
return out;
}
function _internal_set_ps1(args, windowID) {
windowInformation[windowID].ps1Override = args.split("|")[0];
WINDOWS[windowID].element.querySelector(".window [data-type-area]").innerHTML = args.split("|")[1];
return "";
}
function _internal_neofetch(args, windowID) {
WINDOWS[windowID].element.querySelector(".window [data-type-area]").innerHTML = "neofetch";
return _internal_neofetchOutputs[args];
}
const _internal_commands = {
cat: cat,
cd: cd,
clear: clear,
help: help,
ls: ls,
exit: exit,
_internal_set_ps1: _internal_set_ps1,
_internal_neofetch: _internal_neofetch
};
const _internal_defaultFiles = {
about: `hi there! i'm trinkey!
--------------------
i'm a silly little kitty cat who lives in the usa.
i'm trans (she/her, they/them and it/its are also fine).
i'm not actively in a relationship, however i'm also not looking to get into one either.
--------------------
i like to code stuff (mostly websites)! some of my programs can be found on the
projects page. i know a few languages, those being python, javascript/typescript, html/css (if you count those), and a little bit of java.
--------------------
well, that's about it! i hope you like my website!
`,
socials: `- fedi - @trinkey@trinkey.com (or @trinkey@is.trinkey.com)
- signal - @trinkey.01
- email - trinkey [at] proton [dot] me
`,
buttons: `my button: (click to copy html)
--------------------
cool people:
`,
testimonials: `"warning: this user is trinkey"
"This user is only slightly crazy once was I. 10/10 would recommend"
"the f slur but repeated 36 times"
`,
webrings: `
`,
projects: `projects - the things i made
-
dotindex (
pypi) - a python library that lets you access dicts using the dot notation (dict.key) instead of whatever python does (dict["key"])
-
tSuite (
website) - a collection of services that are all interconnected
i'll likely add more in the future, these are just the ones i'm most proud of at the moment.
`,
directory: `there's a lot that goes into this website. here are some links for your usage to help you navigate this hellhole
this is where you are right now
the frontend i use for
fedi
authentication manager for tSuite
tBlog, from tSuite
frontend to a fedi bot that anyone can post to (@everyonebot@is.trinkey.com)
tMessage, from tSuite
has some music. i haven't actually updated the site in a while but i've been meaning to do rewrite it at some point. 100% legal i pinky promise
official jerimiah smiggins instance, that being my own social media platform
(asterisk (*) means i haven't written the code for it)
`
};
const _internal_neofetchOutputs = {
desktop: ` ...-:::::-... trinkey@desktop
.-MMMMMMMMMMMMMMM-. ---------------
.-MMMM\`..-:::::::-..\`MMMM-. OS: Linux Mint 21.3 x86_64
.:MMMM.:MMMMMMMMMMMMMMM:.MMMM:. Host: MS-7E27 1.0
-MMM-M---MMMMMMMMMMMMMMMMMMM.MMM- Resolution: 1680x1050, 2560x1440
\`:MMM:MM\` :MMMM:....::-...-MMMM:MMM:\` DE: Cinnamon 6.0.4
:MMM:MMM\` :MM:\` \`\` \`\` \`:MMM:MMM: WM: Mutter (Muffin)
.MMM.MMMM\` :MM. -MM. .MM- \`MMMM.MMM. CPU: AMD Ryzen 9 7950X (32) @ 5.881GHz
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM-MMM: GPU: AMD ATI 03:00.0 Device 747e
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM:MMM: GPU: AMD ATI 11:00.0 Device 164e
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM-MMM: Memory: 1MiB / 127901MiB
.MMM.MMMM\` :MM:--:MM:--:MM: \`MMMM.MMM.
:MMM:MMM- \`-MMMMMMMMMMMM-\` -MMM-MMM:
:MMM:MMM:\` \`:MMM:MMM:
.MMM.MMMM:--------------:MMMM.MMM.
'-MMMM.-MMMMMMMMMMMMMMM-.MMMM-'
'.-MMMM\`\`--:::::--\`\`MMMM-.'
'-MMMMMMMMMMMMM-'
\`\`-:::::-\`\`
`,
laptop: ` ...-:::::-... trinkey@laptop
.-MMMMMMMMMMMMMMM-. ---------------
.-MMMM\`..-:::::::-..\`MMMM-. OS: Linux Mint 21.2 x86_64
.:MMMM.:MMMMMMMMMMMMMMM:.MMMM:. Host: Dell G15 5510
-MMM-M---MMMMMMMMMMMMMMMMMMM.MMM- Resolution: 2560x1440, 1920x1080, 1680x1050
\`:MMM:MM\` :MMMM:....::-...-MMMM:MMM:\` DE: Cinnamon 5.8.4
:MMM:MMM\` :MM:\` \`\` \`\` \`:MMM:MMM: WM: Mutter (Muffin)
.MMM.MMMM\` :MM. -MM. .MM- \`MMMM.MMM. CPU: Intel i5-10500H (12) @ 4.500GHz
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM-MMM: GPU: NVIDIA GeForce RTX 3050 Ti Mobile
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM:MMM: GPU: Intel CometLake-H GT2 [UHD Graphics]
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM-MMM: Memory: 2001MiB / 15765MiB
.MMM.MMMM\` :MM:--:MM:--:MM: \`MMMM.MMM.
:MMM:MMM- \`-MMMMMMMMMMMM-\` -MMM-MMM:
:MMM:MMM:\` \`:MMM:MMM:
.MMM.MMMM:--------------:MMMM.MMM.
'-MMMM.-MMMMMMMMMMMMMMM-.MMMM-'
'.-MMMM\`\`--:::::--\`\`MMMM-.'
'-MMMMMMMMMMMMM-'
\`\`-:::::-\`\`
`,
server: ` ...-:::::-... trinkey@server
.-MMMMMMMMMMMMMMM-. ---------------
.-MMMM\`..-:::::::-..\`MMMM-. OS: Linux Mint 21.2 x86_64
.:MMMM.:MMMMMMMMMMMMMMM:.MMMM:. Host: Macmini7,1 1.0
-MMM-M---MMMMMMMMMMMMMMMMMMM.MMM- CPU: Intel i5-4278U (4) @ 3.100GHz
\`:MMM:MM\` :MMMM:....::-...-MMMM:MMM:\` GPU: Intel Haswell-ULT
:MMM:MMM\` :MM:\` \`\` \`\` \`:MMM:MMM: Memory: 9011MiB / 15866MiB
.MMM.MMMM\` :MM. -MM. .MM- \`MMMM.MMM.
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM-MMM:
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM:MMM:
:MMM:MMMM\` :MM. -MM- .MM: \`MMMM-MMM:
.MMM.MMMM\` :MM:--:MM:--:MM: \`MMMM.MMM.
:MMM:MMM- \`-MMMMMMMMMMMM-\` -MMM-MMM:
:MMM:MMM:\` \`:MMM:MMM:
.MMM.MMMM:--------------:MMMM.MMM.
'-MMMM.-MMMMMMMMMMMMMMM-.MMMM-'
'.-MMMM\`\`--:::::--\`\`MMMM-.'
'-MMMMMMMMMMMMM-'
\`\`-:::::-\`\`
`
};
const helpText = ` -=== tSh help ===-
--------------------
-= cat =-
Displays the contents of a file.
-= cd =-
Changes the working directory.
-= clear =-
Clears the terminal output.
-= help =-
Shows this help menu.
-= ls =-
Lists all files in a directory.
-l - displays more information about each file
-a - displays all files
-A - displays all files except implied . and ..
-r - reverses the order of the files
-R - recurse through all subdirectories
-= exit =-
Closes the terminal.
`;
const HOME_DIR = "/home/trinkey";
let FILESYSTEM = {
home: {
type: "directory",
name: "home",
files: {
trinkey: {
type: "directory",
name: "trinkey",
files: {
people: { type: "directory", name: "people", files: {
"88x31.txt": { type: "file", name: "88x31.txt", content: _internal_defaultFiles.buttons },
"testimonials.txt": { type: "file", name: "testimonials.txt", content: _internal_defaultFiles.testimonials },
"webrings.txt": { type: "file", name: "webrings.txt", content: _internal_defaultFiles.webrings }
} },
"about-me.txt": { type: "file", name: "about-me.txt", content: _internal_defaultFiles.about },
"socials.txt": { type: "file", name: "socials.txt", content: _internal_defaultFiles.socials },
"projects.txt": { type: "file", name: "projects.txt", content: _internal_defaultFiles.projects },
"subdomains.txt": { type: "file", name: "subdomains.txt", content: _internal_defaultFiles.directory }
}
}
}
},
bin: {
type: "directory",
name: "bin",
files: {
cat: { type: "file", name: "cat", content: "function cat(file: string): string { ... }
" },
cd: { type: "file", name: "cd", content: "function cd(directory: string): void { ... }
" },
clear: { type: "file", name: "clear", content: "function clear(): void { ... }
" },
help: { type: "file", name: "help", content: "function help(): string { ... }
" },
ls: { type: "file", name: "ls", content: "function ls(directory: string): string { ... }
" },
neofetch: { type: "file", name: "neofetch", content: "function neofetch(): string { ... }
" }
}
},
".secret-file": { type: "file", name: ".secret-file", content: "meow :3
" }
};
let windowInformation = {};
function commandManager(windowID, command) {
if (!windowInformation[windowID]) {
windowInformation[windowID] = {
PWD: HOME_DIR
};
}
let out;
if (_internal_commands[command.split(" ")[0]]) {
out = _internal_commands[command.split(" ")[0]](command.split(" ").slice(1).join(" ").trim(), windowID);
}
else if (command == "") {
out = "";
}
else {
out = `Unknown command '${escapeHTML(command.split(" ")[0])}'.
Type 'help' for a list of commands
`;
}
let el = document.createElement("div");
el.innerHTML = out;
WINDOWS[windowID].element.querySelector(".window").append(el);
let dTE = WINDOWS[windowID].element.querySelector("[data-type-area]");
if (dTE) {
dTE.removeAttribute("data-type-area");
if (dTE.querySelector("i.cursor")) {
dTE.querySelector("i.cursor").remove();
}
else if (dTE.querySelector(".cursor")) {
dTE.querySelector(".cursor").classList.remove("cursor");
}
}
WINDOWS[windowID].element.querySelector(".window-header-title").innerText = `${_internal_stringifyPath(windowInformation[windowID].PWD)} - tSh`;
let ps1 = document.createElement("div");
ps1.innerHTML = _internal_getPS1(windowInformation[windowID]);
let typeArea = document.createElement("span");
typeArea.dataset.typeArea = "";
typeArea.innerHTML = " ";
ps1.append(typeArea);
WINDOWS[windowID].element.querySelector(".window").append(ps1);
}