tab completions + more testimonials + velzie button

This commit is contained in:
trinkey 2025-03-10 18:09:59 -04:00
parent c13dfabd47
commit dc4309ae38
8 changed files with 417 additions and 222 deletions

View file

@ -86,6 +86,15 @@ nav {
margin: 0 auto;
}
.emoji {
--emoji-size: 1.1em;
height: var(--emoji-size);
width: var(--emoji-size);
object-fit: contain;
position: relative;
top: 0.2em;
}
.cursor {
color: var(--crust);
background-color: var(--text);

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -47,10 +47,6 @@
<nav>
<div class="header-title">trinkey's website!</div>
<div><a href="javascript:emptyWindow()">open terminal</a></div>
<div class="hyphen">-</div>
<div><a href="javascript:windowPreset('specs', true)">specs</a></div>
<div class="hyphen">-</div>
<div><a href="javascript:createBlob()">spin</a></div>
</nav>
</header>
@ -59,7 +55,7 @@
<a href="no-js.html">this page</a> instead
</noscript>
<img class="cat" src="img/cat.jpg" alt="My cat">
<img class="cat" src="img/cat.jpg" alt="my cat">
<footer>
<nav>
@ -69,20 +65,6 @@
</nav>
</footer>
<div hidden id="window-templates">
<ol data-template-id="specs">
<li>_internal_set_ps1 trinkey@desktop|ssh trinkey@desktop<br>Last login: Tue Sep 10 12:00:24 2024 from 192.168.1.254</li>
<li>_internal_neofetch desktop</li>
<li>_internal_set_ps1 trinkey@website|exit</li>
<li>_internal_set_ps1 trinkey@server|ssh trinkey@server<br>Last login: Tue Sep 11 9:03:02 2001 from 192.168.1.254</li>
<li>_internal_neofetch server</li>
<li>_internal_set_ps1 trinkey@website|exit</li>
<li>(i never made a way to realistically access this via just the builtin commands yet so this is what you get)</li>
<div data-template-field="width" data-is-number>1000</div>
<div data-template-field="height" data-is-number>800</div>
</ol>
</div>
<script src="js/index.js"></script>
<script src="js/blobcat.js"></script>
</body>

View file

@ -53,6 +53,74 @@ function edgeMoveEvent(x, y, pos, windowID) {
w.element.querySelector(".window").style.width = `${w.width}px`;
w.element.querySelector(".window").style.height = `${w.height}px`;
}
function mouseMoveEvent(windowID, x, y) {
let w = WINDOWS[windowID];
w.posX = Math.max(0, Math.min(innerWidth - w.width - _windowPaddingX, x - w.vars.mouseOffsetX));
w.posY = Math.max(0, Math.min(innerHeight - w.height - _windowPaddingY, y - w.vars.mouseOffsetY));
w.element.style.left = `${w.posX}px`;
w.element.style.top = `${w.posY}px`;
}
function syncInputs(windowID) {
let windowInput = WINDOWS[windowID].element.querySelector("input.window-input");
let windowVisualText = WINDOWS[windowID].element.querySelector("[data-type-area]");
let w = WINDOWS[windowID].element.querySelector(".window");
if (!windowVisualText) {
return;
}
setTimeout(function () {
let text = windowInput.value;
let cursor = windowInput.selectionStart;
if (cursor == text.length) {
windowVisualText.innerHTML = `${escapeHTML(text)}<i class="cursor">&nbsp;</i>`;
}
else {
windowVisualText.innerHTML = `${escapeHTML(text.slice(0, cursor))}<span class="cursor">${escapeHTML(text[cursor])}</span>${escapeHTML(text.slice(cursor + 1))}`;
}
}, 1);
w.scrollTop = w.scrollHeight;
}
function setCursor(windowID) {
let windowInput = WINDOWS[windowID].element.querySelector("input.window-input");
setTimeout(() => {
windowInput.setSelectionRange(windowInput.value.length, windowInput.value.length);
syncInputs(windowID);
}, 1);
}
function toggleFullscreen(windowID) {
let w = WINDOWS[windowID];
if (w.fullscreen) {
w.posX = Math.max(0, Math.min(innerWidth - w.vars.oldWidth - _windowPaddingX, w.vars.oldPosX));
w.posY = Math.max(0, Math.min(innerHeight - w.vars.oldHeight - _windowPaddingY, w.vars.oldPosY));
w.width = Math.max(w.minWidth, Math.min(w.vars.oldWidth, innerWidth - _windowPaddingX));
w.height = Math.max(w.minHeight, Math.min(w.vars.oldHeight, innerHeight - _windowPaddingY));
w.fullscreen = false;
delete w.vars.oldPosX;
delete w.vars.oldPosY;
delete w.vars.oldWidth;
delete w.vars.oldHeight;
w.element.style.left = `${w.posX}px`;
w.element.style.top = `${w.posY}px`;
w.element.style.width = `${w.width + _windowPaddingX - 2}px`;
w.element.querySelector(".window").style.width = `${w.width}px`;
w.element.querySelector(".window").style.height = `${w.height}px`;
}
else {
w.vars.oldPosX = w.posX;
w.vars.oldPosY = w.posY;
w.vars.oldWidth = w.width;
w.vars.oldHeight = w.height;
w.fullscreen = true;
w.posX = 0;
w.posY = 0;
w.width = innerWidth;
w.height = innerHeight;
w.element.style.left = "0px";
w.element.style.top = "0px";
w.element.style.width = `${w.width}px`;
w.element.querySelector(".window").style.width = `${w.width - _windowPaddingX}px`;
w.element.querySelector(".window").style.height = `${w.height - _windowPaddingY}px`;
}
}
function createWindow(config) {
if (document.getElementById(config.id)) {
incrementZIndex(config.id);
@ -90,30 +158,8 @@ function createWindow(config) {
wI = document.createElement("input");
wI.classList.add("window-input");
wI.id = `${config.id}__input`;
function syncInputs() {
setTimeout(function () {
let text = wI.value;
let cursor = wI.selectionStart;
let el = wC.querySelector("[data-type-area]");
if (!el) {
return;
}
if (cursor == text.length) {
el.innerHTML = `${escapeHTML(text)}<i class="cursor">&nbsp;</i>`;
}
else {
el.innerHTML = `${escapeHTML(text.slice(0, cursor))}<span class="cursor">${escapeHTML(text[cursor])}</span>${escapeHTML(text.slice(cursor + 1))}`;
}
}, 1);
}
function setCursor() {
setTimeout(() => {
wI.setSelectionRange(wI.value.length, wI.value.length);
syncInputs();
}, 0);
}
wI.oninput = (event) => {
syncInputs();
syncInputs(config.id);
w.scrollTop = w.scrollHeight;
};
wI.onkeydown = (event) => {
@ -122,12 +168,88 @@ function createWindow(config) {
w.scrollTop = w.scrollHeight;
wI.value = "";
}
else if (event.key == "Tab") {
event.preventDefault();
let val = wI.value.trim();
let possibilities = [];
let parent;
if (!val) {
return;
}
if (val.split(" ").length == 1 && wI.value[wI.value.length - 1] != " ") {
possibilities = Object.keys(_internal_commands).filter((cmd) => (cmd.startsWith(val) && !cmd.startsWith("_")));
}
else if (_internal_commands[val.split(" ")[0]] && _internal_commands[val.split(" ")[0]].autocomplete) {
let ac = _internal_commands[val.split(" ")[0]].autocomplete;
let path = val.split(" ").slice(1).join(" ").trim();
let sw = path.split("/")[path.split("/").length - 1];
if (typeof ac == "object") {
possibilities = ac;
}
else {
syncInputs();
if (path) {
if (path[path.length - 1] == "/") {
parent = _internal_getFile(_internal_sanitizePath(_internal_joinPaths(windowInformation[config.id].PWD, path)));
}
else {
parent = _internal_getFile(_internal_sanitizePath(_internal_joinPaths(windowInformation[config.id].PWD, path + "/..")));
}
}
else {
parent = _internal_getFile(windowInformation[config.id].PWD);
}
if (parent && parent.type == "directory") {
let f = parent.files;
possibilities = Object.keys(f);
if (ac == "dir") {
possibilities = possibilities.filter((file) => (f[file] && f[file].type == "directory"));
}
}
else {
parent = null;
}
}
possibilities = possibilities.filter((v) => v.startsWith(sw));
}
if (possibilities.length == 1) {
if (val.split(" ").length == 1 && wI.value[wI.value.length - 1] != " ") {
wI.value = possibilities[0] + " ";
}
else if (_internal_commands[val.split(" ")[0]] && _internal_commands[val.split(" ")[0]].autocomplete) {
let path = val;
if (val[val.length - 1] == "/") {
path += possibilities[0];
}
else {
let p = path.split("/");
if (p.length == 1) {
p = p[0].split(" ", 2);
if (p.length == 1) {
p.push("");
}
p.pop();
path = p.join(" ") + " " + possibilities[0];
}
else {
p.pop();
path = p.join("/") + "/" + possibilities[0];
}
}
wI.value = path + (parent && parent.type == "directory" && parent.files[possibilities[0]].type == "directory" ? "/" : " ");
}
syncInputs(config.id);
}
else if (possibilities) {
addWindowCommand(config.id, possibilities.join(" "));
syncInputs(config.id);
}
}
else {
syncInputs(config.id);
}
};
wI.onfocus = setCursor;
wI.onclick = setCursor;
wI.onfocus = () => { setCursor(config.id); };
wI.onclick = () => { setCursor(config.id); };
wC = document.createElement("label");
wC.htmlFor = `${config.id}__input`;
}
@ -146,6 +268,7 @@ function createWindow(config) {
let el = document.createElement("div");
el.classList.add("edge", pos);
el.addEventListener("mousedown", function (e) {
incrementZIndex(config.id);
e.preventDefault();
WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX;
WINDOWS[config.id].vars.mouseOffsetY = e.clientY - WINDOWS[config.id].posY;
@ -182,12 +305,9 @@ function createWindow(config) {
zIndex: globalIncrement,
vars: {}
};
function mouseMoveEvent(x, y) {
WINDOWS[config.id].posX = Math.max(0, Math.min(innerWidth - WINDOWS[config.id].width - _windowPaddingX, x - WINDOWS[config.id].vars.mouseOffsetX));
WINDOWS[config.id].posY = Math.max(0, Math.min(innerHeight - WINDOWS[config.id].height - _windowPaddingY, y - WINDOWS[config.id].vars.mouseOffsetY));
wC.style.left = `${WINDOWS[config.id].posX}px`;
wC.style.top = `${WINDOWS[config.id].posY}px`;
}
windowInformation[config.id] = {
PWD: HOME_DIR
};
wC.addEventListener("focus", function () { incrementZIndex(config.id); });
for (const link of wC.querySelectorAll("a")) {
link.addEventListener("focus", function () { incrementZIndex(config.id); });
@ -199,7 +319,7 @@ function createWindow(config) {
WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX;
WINDOWS[config.id].vars.mouseOffsetY = e.clientY - WINDOWS[config.id].posY;
MOUSE_MOVE_PROCESSING[config.id] = {
callback: mouseMoveEvent,
callback: (x, y) => { mouseMoveEvent(config.id, x, y); },
mouseUp: true
};
});
@ -212,41 +332,8 @@ function createWindow(config) {
config.onDestroy();
}
});
wH.querySelector(".fullscreen").addEventListener("click", function () {
let window = WINDOWS[config.id];
if (window.fullscreen) {
window.posX = Math.max(0, Math.min(innerWidth - window.vars.oldWidth - _windowPaddingX, window.vars.oldPosX));
window.posY = Math.max(0, Math.min(innerHeight - window.vars.oldHeight - _windowPaddingY, window.vars.oldPosY));
window.width = Math.max(window.minWidth, Math.min(window.vars.oldWidth, innerWidth - _windowPaddingX));
window.height = Math.max(window.minHeight, Math.min(window.vars.oldHeight, innerHeight - _windowPaddingY));
window.fullscreen = false;
delete window.vars.oldPosX;
delete window.vars.oldPosY;
delete window.vars.oldWidth;
delete window.vars.oldHeight;
wC.style.left = `${window.posX}px`;
wC.style.top = `${window.posY}px`;
wC.style.width = `${window.width + _windowPaddingX - 2}px`;
w.style.width = `${window.width}px`;
w.style.height = `${window.height}px`;
}
else {
window.vars.oldPosX = window.posX;
window.vars.oldPosY = window.posY;
window.vars.oldWidth = window.width;
window.vars.oldHeight = window.height;
window.fullscreen = true;
window.posX = 0;
window.posY = 0;
window.width = innerWidth;
window.height = innerHeight;
wC.style.left = "0px";
wC.style.top = "0px";
wC.style.width = `${window.width}px`;
w.style.width = `${window.width - _windowPaddingX}px`;
w.style.height = `${window.height - _windowPaddingY}px`;
}
});
wH.querySelector(".fullscreen").addEventListener("click", () => (toggleFullscreen(config.id)));
wH.addEventListener("dblclick", () => (toggleFullscreen(config.id)));
globalIncrement++;
}
function windowPreset(template, dontDisableTyping = false) {

View file

@ -209,19 +209,21 @@ function _internal_neofetch(args, windowID) {
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
cat: { callback: cat, autocomplete: "file" },
cd: { callback: cd, autocomplete: "dir" },
clear: { callback: clear, autocomplete: null },
help: { callback: help, autocomplete: null },
ls: { callback: ls, autocomplete: "file" },
exit: { callback: exit, autocomplete: null },
_internal_set_ps1: { callback: _internal_set_ps1, autocomplete: null },
_internal_neofetch: { callback: _internal_neofetch, autocomplete: null },
spin: { callback: (args, windowID) => { createBlob(); return ""; }, autocomplete: null },
};
const _internal_defaultFiles = {
about: `<div><b>hi there! i'm trinkey!</b></div>
about: `<div><b>hi there!</b></div>
<div>i'm trinkey, but you can call me <b>katie</b> too! <small>(trinkey is more of a username)</small></div>
<div>--------------------</div>
<div>i'm a silly little kitty cat who lives in the usa (sadly).</div>
<div>i'm a silly little cat who lives in the usa (new york time zone).</div>
<div>i'm <span class="blue">t</span><span class="pink">r</span>a<span class="pink">n</span><span class="blue">s</span> (she/it, they/them is also fine).</div>
<div>i'm not actively in a relationship, however i'm also not looking to get into one either.</div>
<div>--------------------</div>
@ -236,6 +238,7 @@ const _internal_defaultFiles = {
<div>- signal - <b>@trinkey.01</b></div>
<div>- email - <b>trinkey [at] proton [dot] me</b></div>
<div>- youtube - <a href="https://youtube.com/@trinkey" target="_blank"><b>@trinkey</b></a> (inactive)</div>
<div>- discord - <b>@trinkey_</b> (mostly inactive)</div>
<div>- <a href="trinkey_gpg.asc" target="_blank">gpg key</a> (1D6E 5D28 BDD4 D7FA 96B8 8799 2B33 C6C6 14F2 591A)</div>`,
projects: `<div><b>projects</b> - the things i made</div>
<div>- <a href="https://github.com/jerimiah-smiggins/smiggins/" target="_blank"><b>smiggins</b></a> (<a href="https://smiggins.trinkey.com/" target="_blank">website</a>) - a social media platform i made</div>
@ -247,23 +250,28 @@ const _internal_defaultFiles = {
buttons: `<div><b>my button:</b> (click to copy html)</div>
<div><img style="cursor: pointer;" src="img/88x31.png" alt="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." title="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." onclick="copyButton()"></div>
<div>--------------------</div>
<div><b>cool people:</b></div>
<div class="buttons-88x31">
<a href="https://notfire.cc" target="_blank"><img src="https://notfire.cc/design/images/buttons/notfire-cc-88x31-af.gif" alt="notfire.cc" title="notfire.cc"></a>
<a href="https://micro.niko.lgbt" target="_blank"><img src="https://micro.niko.lgbt/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;"></a>
<a href="https://microspinny.zip" target="_blank"><img src="https://microspinny.zip/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;"></a>
<a href="https://w.on-t.work" target="_blank"><img src="https://w.on-t.work/assets/88x31.png" alt="kopper's button" title="kopper's button"></a>
<a href="https://synth.download" target="_blank"><img src="https://synth.download/assets/buttons/sneexy.svg" alt="Sneexy" title="Sneexy"></a>
<a href="https://beepi.ng" target="_blank"><img src="https://beepi.ng/88x31.png" alt="unnick" title="unnick"></a>
<a href="http://autumn.town" target="_blank"><img src="https://autumn.town/assets/buttons/mybutton.webp" alt="Autumn Town Café" title="Autumn Town Café"></a>
<a href="https://redcatho.de" target="_blank"><img src="https://redcatho.de/buttons/red.png" alt="the text 'red is purple' on a purple background" title="the text 'red is purple' on a purple background"></a>
<a href="https://doskel.net" target="_blank"><img src="https://doskel.net/button.png" alt="doskel" title="doskel"></a>
<a href="https://velzie.rip" target="_blank"><img src="https://velzie.rip/88x31.png" alt="velzie" title="velzie"></a>
</div>`,
testimonials: `<div>"warning: this user is trinkey"</div>
<div>- <a href="https://booping.synth.download/@breaadyboy" target="_blank">bread</a></div><br>
<div>"This user is only slightly crazy once was I. 10/10 would recommend"</div>
<div>- <a href="https://lea.pet/@subroutine" target="_blank">subroutine</a></div><br>
<div>"the f slur but repeated 36 times"</div>
<div>- <a href="https://oomfie.city/@cornfields74">corn fields seventy four</a></div>`,
<div>- <a href="https://oomfie.city/@cornfields74" target="_blank">corn fields seventy four</a></div><br>
<div>"silly cute kitn"</div>
<div>- <a href="https://microspinny.zip" target="_blank">niko</a></div><br>
<div>"very bitable <img class="emoji" alt="neodog_bite_neocat" src="img/emoji/neodog_bite_neocat.png"></div>
<div>- <a href="https://booping.synth.download/@strongsand" target="_blank">strongsand</a></div>
`,
webrings: `<div>
<a href="https://ctp-webr.ing/trinkey/previous">&larr;</a>
<a href="https://ctp-webr.ing/">catppuccin webring</a>
@ -301,6 +309,7 @@ const _internal_defaultFiles = {
<div>the <a href="https://codeberg.org/KittyShopper/mastoapi-fe">outpost</a> frontend for fedi made by kopper</div><br>
<div><b><a href="https://smiggins.trinkey.com/">smiggins.trinkey.com</a>:</b></div>
<div>official jerimiah smiggins instance, that being my own social media platform</div><br>
<div>there's also a qna for me at *<a href="https://trinkey.com/qna/index.php">trinkey.com/qna</a></div><br>
<div>(asterisk (*) means i haven't written the code for it)</div><br>`
};
const _internal_neofetchOutputs = {
@ -343,25 +352,28 @@ const _internal_neofetchOutputs = {
<b>'-MMMMMMMMMMMMM-'</b>
<b>\`\`-:::::-\`\`</b></pre>`
};
const helpText = `<div>&nbsp;-=== <b class="pink">tSh help</b> ===-</div>
const helpText = `
<div>&nbsp;-=== <b class="pink">tSh help</b> ===-</div>
<div>--------------------</div>
<div>-= <b class="green">cat</b> =-</div>
<div>Displays the contents of a file.</div>
<div>displays the contents of a file</div>
<div>-= <b class="green">cd</b> =-</div>
<div>Changes the working directory.</div>
<div>changes the working directory</div>
<div>-= <b class="green">clear</b> =-</div>
<div>Clears the terminal output.</div>
<div>clears the terminal output</div>
<div>-= <b class="green">help</b> =-</div>
<div>Shows this help menu.</div>
<div>shows this help menu</div>
<div>-= <b class="green">ls</b> =-</div>
<div>Lists all files in a directory.</div>
<div>lists all files in a directory</div>
<div>&nbsp; -l - displays more information about each file</div>
<div>&nbsp; -a - displays all files</div>
<div>&nbsp; -A - displays all files except implied . and ..</div>
<div>&nbsp; -r - reverses the order of the files</div>
<div>&nbsp; -R - recurse through all subdirectories</div>
<div>-= <b class="green">spin</b> =-</div>
<div>creates a new window with a spinning blobcat wireframe</div>
<div>-= <b class="green">exit</b> =-</div>
<div>Closes the terminal.</div>`;
<div>closes the terminal window</div>`;
const HOME_DIR = "/home/trinkey";
let FILESYSTEM = {
home: {
@ -393,22 +405,16 @@ let FILESYSTEM = {
cd: { type: "file", name: "cd", content: "<div>function cd(directory: string): void { ... }</div>" },
clear: { type: "file", name: "clear", content: "<div>function clear(): void { ... }</div>" },
help: { type: "file", name: "help", content: "<div>function help(): string { ... }</div>" },
ls: { type: "file", name: "ls", content: "<div>function ls(directory: string): string { ... }</div>" },
neofetch: { type: "file", name: "neofetch", content: "<div>function neofetch(): string { ... }</div>" }
ls: { type: "file", name: "ls", content: "<div>function ls(directory?: string): string { ... }</div>" }
}
},
".secret-file": { type: "file", name: ".secret-file", content: "<div>meow :3</div>" }
};
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);
out = _internal_commands[command.split(" ")[0]].callback(command.split(" ").slice(1).join(" ").trim(), windowID);
}
else if (command == "") {
out = "";
@ -416,8 +422,11 @@ function commandManager(windowID, command) {
else {
out = `<div class="red">Unknown command '${escapeHTML(command.split(" ")[0])}'.</div><div>Type 'help' for a list of commands</div>`;
}
addWindowCommand(windowID, out);
}
function addWindowCommand(windowID, value) {
let el = document.createElement("div");
el.innerHTML = out;
el.innerHTML = value;
WINDOWS[windowID].element.querySelector(".window").append(el);
let dTE = WINDOWS[windowID].element.querySelector("[data-type-area]");
if (dTE) {

View file

@ -61,10 +61,9 @@
<div>html:</div>
<code style="overflow-x: scroll; display: inline-block; background-color: var(--mantle); padding: 5px 8px; border-radius: 5px;">&lt;a href=&quot;https://trinkey.com/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://trinkey.com/img/88x31.png&quot; alt=&quot;trinkey&apos;s 88x31. image of her cat on the right with the word trinkey name taking up the rest of the button.&quot; title=&quot;trinkey&apos;s 88x31. image of her cat on the right with the word trinkey name taking up the rest of the button.&quot;&gt;&lt;/a&gt;</code>
<div>--------------------</div>
<h3>cool people:</h3>
<div class="buttons-88x31">
<a href="https://notfire.cc" target="_blank"><img src="https://notfire.cc/design/images/buttons/notfire-cc-88x31-af.gif" alt="notfire.cc"></a>
<a href="https://micro.niko.lgbt" target="_blank"><img src="https://micro.niko.lgbt/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;"></a>
<a href="https://microspinny.zip" target="_blank"><img src="https://microspinny.zip/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;"></a>
<a href="https://w.on-t.work" target="_blank"><img src="https://w.on-t.work/assets/88x31.png" alt="kopper's button" title="kopper's button"></a>
<a href="https://synth.download" target="_blank"><img src="https://synth.download/assets/buttons/sneexy.svg" alt="Sneexy" title="Sneexy"></a>
<a href="https://beepi.ng" target="_blank"><img src="https://beepi.ng/88x31.png" alt="unnick" title="unnick"></a>
@ -119,6 +118,7 @@
<div>the <a href="https://codeberg.org/KittyShopper/mastoapi-fe">outpost</a> frontend for fedi made by kopper</div><br>
<div><b><a href="https://smiggins.trinkey.com/">smiggins.trinkey.com</a>:</b></div>
<div>official jerimiah smiggins instance, that being my own social media platform</div><br>
<div>there's also a qna for me at *<a href="https://trinkey.com/qna/index.php">trinkey.com/qna</a></div><br>
<div>(asterisk (*) means i haven't written the code for it)</div>
</div>
</body>

View file

@ -68,6 +68,79 @@ function edgeMoveEvent(x: number, y: number, pos: "top" | "bottom" | "left" | "r
(w.element.querySelector(".window") as HTMLElement).style.height = `${w.height}px`;
}
function mouseMoveEvent(windowID: string, x: number, y: number): void {
let w: _winConf = WINDOWS[windowID];
w.posX = Math.max(0, Math.min(innerWidth - w.width - _windowPaddingX, x - w.vars.mouseOffsetX));
w.posY = Math.max(0, Math.min(innerHeight - w.height - _windowPaddingY, y - w.vars.mouseOffsetY));
w.element.style.left = `${w.posX}px`;
w.element.style.top = `${w.posY}px`;
}
function syncInputs(windowID: string): void {
let windowInput: HTMLInputElement = WINDOWS[windowID].element.querySelector("input.window-input");
let windowVisualText: HTMLDivElement = WINDOWS[windowID].element.querySelector("[data-type-area]");
let w: HTMLDivElement = WINDOWS[windowID].element.querySelector(".window");
if (!windowVisualText) { return; }
setTimeout(function(): void {
let text: string = windowInput.value;
let cursor: number = windowInput.selectionStart;
if (cursor == text.length) {
windowVisualText.innerHTML = `${escapeHTML(text)}<i class="cursor">&nbsp;</i>`;
} else {
windowVisualText.innerHTML = `${escapeHTML(text.slice(0, cursor))}<span class="cursor">${escapeHTML(text[cursor])}</span>${escapeHTML(text.slice(cursor + 1))}`;
}
}, 1);
w.scrollTop = w.scrollHeight;
}
function setCursor(windowID: string): void {
let windowInput: HTMLInputElement = WINDOWS[windowID].element.querySelector("input.window-input");
setTimeout((): void => {
windowInput.setSelectionRange(windowInput.value.length, windowInput.value.length);
syncInputs(windowID);
}, 1);
}
function toggleFullscreen(windowID: string): void {
let w: _winConf = WINDOWS[windowID];
if (w.fullscreen) {
w.posX = Math.max(0, Math.min(innerWidth - w.vars.oldWidth - _windowPaddingX, w.vars.oldPosX)); // window.vars.oldPosX;
w.posY = Math.max(0, Math.min(innerHeight - w.vars.oldHeight - _windowPaddingY, w.vars.oldPosY)); // window.vars.oldPosY;
w.width = Math.max(w.minWidth, Math.min(w.vars.oldWidth, innerWidth - _windowPaddingX)); // window.vars.oldWidth;
w.height = Math.max(w.minHeight, Math.min(w.vars.oldHeight, innerHeight - _windowPaddingY)); // window.vars.oldHeight;
w.fullscreen = false;
delete w.vars.oldPosX;
delete w.vars.oldPosY;
delete w.vars.oldWidth;
delete w.vars.oldHeight;
w.element.style.left = `${w.posX}px`;
w.element.style.top = `${w.posY}px`;
w.element.style.width = `${w.width + _windowPaddingX - 2}px`;
(w.element.querySelector(".window") as HTMLElement).style.width = `${w.width}px`;
(w.element.querySelector(".window") as HTMLElement).style.height = `${w.height}px`;
} else {
w.vars.oldPosX = w.posX;
w.vars.oldPosY = w.posY;
w.vars.oldWidth = w.width;
w.vars.oldHeight = w.height;
w.fullscreen = true;
w.posX = 0;
w.posY = 0;
w.width = innerWidth;
w.height = innerHeight;
w.element.style.left = "0px";
w.element.style.top = "0px";
w.element.style.width = `${w.width}px`;
(w.element.querySelector(".window") as HTMLElement).style.width = `${w.width - _windowPaddingX}px`;
(w.element.querySelector(".window") as HTMLElement).style.height = `${w.height - _windowPaddingY}px`;
}
}
function createWindow(config: _winInitConf): void {
if (document.getElementById(config.id)) {
incrementZIndex(config.id);
@ -114,31 +187,8 @@ function createWindow(config: _winInitConf): void {
wI.classList.add("window-input");
wI.id = `${config.id}__input`;
function syncInputs(): void {
setTimeout(function(): void {
let text: string = wI.value;
let cursor: number = wI.selectionStart;
let el: HTMLElement = wC.querySelector("[data-type-area]");
if (!el) { return; }
if (cursor == text.length) {
el.innerHTML = `${escapeHTML(text)}<i class="cursor">&nbsp;</i>`;
} else {
el.innerHTML = `${escapeHTML(text.slice(0, cursor))}<span class="cursor">${escapeHTML(text[cursor])}</span>${escapeHTML(text.slice(cursor + 1))}`;
}
}, 1);
}
function setCursor(): void {
setTimeout((): void => {
wI.setSelectionRange(wI.value.length, wI.value.length);
syncInputs();
}, 0);
}
wI.oninput = (event: KeyboardEvent): void => {
syncInputs();
syncInputs(config.id);
w.scrollTop = w.scrollHeight;
};
@ -147,13 +197,91 @@ function createWindow(config: _winInitConf): void {
commandManager(config.id, wI.value.trim());
w.scrollTop = w.scrollHeight;
wI.value = "";
} else if (event.key == "Tab") {
event.preventDefault();
let val: string = wI.value.trim();
let possibilities: string[] = [];
let parent: _file | null;
if (!val) { return; }
if (val.split(" ").length == 1 && wI.value[wI.value.length - 1] != " ") {
possibilities = Object.keys(_internal_commands).filter((cmd: string): boolean => (cmd.startsWith(val) && !cmd.startsWith("_")));
} else if (_internal_commands[val.split(" ")[0]] && _internal_commands[val.split(" ")[0]].autocomplete) {
let ac: "dir" | "file" | string[] = _internal_commands[val.split(" ")[0]].autocomplete;
let path: string = val.split(" ").slice(1).join(" ").trim();
let sw: string = path.split("/")[path.split("/").length - 1];
if (typeof ac == "object") {
possibilities = ac;
} else {
syncInputs();
if (path) {
if (path[path.length - 1] == "/") {
parent = _internal_getFile(_internal_sanitizePath(_internal_joinPaths(windowInformation[config.id].PWD, path)));
} else {
parent = _internal_getFile(_internal_sanitizePath(_internal_joinPaths(windowInformation[config.id].PWD, path + "/..")));
}
} else {
parent = _internal_getFile(windowInformation[config.id].PWD);
}
if (parent && parent.type == "directory") {
let f: _files = parent.files;
possibilities = Object.keys(f);
if (ac == "dir") {
possibilities = possibilities.filter((file: string): boolean => (f[file] && f[file].type == "directory"));
}
} else {
parent = null;
}
}
possibilities = possibilities.filter((v: string): boolean => v.startsWith(sw));
}
if (possibilities.length == 1) {
if (val.split(" ").length == 1 && wI.value[wI.value.length - 1] != " ") {
wI.value = possibilities[0] + " ";
} else if (_internal_commands[val.split(" ")[0]] && _internal_commands[val.split(" ")[0]].autocomplete) {
let path: string = val;
if (val[val.length - 1] == "/") {
path += possibilities[0];
} else {
let p: string[] = path.split("/");
if (p.length == 1) {
p = p[0].split(" ", 2);
if (p.length == 1) {
p.push("");
}
p.pop();
path = p.join(" ") + " " + possibilities[0];
} else {
p.pop();
path = p.join("/") + "/" + possibilities[0];
}
}
wI.value = path + (parent && parent.type == "directory" && parent.files[possibilities[0]].type == "directory" ? "/" : " ");
}
syncInputs(config.id);
} else if (possibilities) {
addWindowCommand(config.id, possibilities.join(" "));
syncInputs(config.id);
}
} else {
syncInputs(config.id);
}
};
wI.onfocus = setCursor;
wI.onclick = setCursor;
wI.onfocus = (): void => { setCursor(config.id); };
wI.onclick = (): void => { setCursor(config.id); };
wC = document.createElement("label");
wC.htmlFor = `${config.id}__input`;
@ -174,6 +302,7 @@ function createWindow(config: _winInitConf): void {
let el: HTMLDivElement = document.createElement("div");
el.classList.add("edge", pos);
el.addEventListener("mousedown", function(e: MouseEvent): void {
incrementZIndex(config.id);
e.preventDefault();
WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX;
@ -215,12 +344,9 @@ function createWindow(config: _winInitConf): void {
vars: {}
};
function mouseMoveEvent(x: number, y: number): void {
WINDOWS[config.id].posX = Math.max(0, Math.min(innerWidth - WINDOWS[config.id].width - _windowPaddingX, x - WINDOWS[config.id].vars.mouseOffsetX));
WINDOWS[config.id].posY = Math.max(0, Math.min(innerHeight - WINDOWS[config.id].height - _windowPaddingY, y - WINDOWS[config.id].vars.mouseOffsetY));
wC.style.left = `${WINDOWS[config.id].posX}px`;
wC.style.top = `${WINDOWS[config.id].posY}px`;
}
windowInformation[config.id] = {
PWD: HOME_DIR
};
// wC.addEventListener("mousedown", function(): void { incrementZIndex(config.id); });
wC.addEventListener("focus", function(): void { incrementZIndex(config.id); });
@ -237,7 +363,7 @@ function createWindow(config: _winInitConf): void {
WINDOWS[config.id].vars.mouseOffsetX = e.clientX - WINDOWS[config.id].posX;
WINDOWS[config.id].vars.mouseOffsetY = e.clientY - WINDOWS[config.id].posY;
MOUSE_MOVE_PROCESSING[config.id] = {
callback: mouseMoveEvent,
callback: (x: number, y: number): void => { mouseMoveEvent(config.id, x, y); },
mouseUp: true
};
});
@ -253,40 +379,8 @@ function createWindow(config: _winInitConf): void {
}
});
wH.querySelector(".fullscreen").addEventListener("click", function(): void {
let window: _winConf = WINDOWS[config.id];
if (window.fullscreen) {
window.posX = Math.max(0, Math.min(innerWidth - window.vars.oldWidth - _windowPaddingX, window.vars.oldPosX)); // window.vars.oldPosX;
window.posY = Math.max(0, Math.min(innerHeight - window.vars.oldHeight - _windowPaddingY, window.vars.oldPosY)); // window.vars.oldPosY;
window.width = Math.max(window.minWidth, Math.min(window.vars.oldWidth, innerWidth - _windowPaddingX)); // window.vars.oldWidth;
window.height = Math.max(window.minHeight, Math.min(window.vars.oldHeight, innerHeight - _windowPaddingY)); // window.vars.oldHeight;
window.fullscreen = false;
delete window.vars.oldPosX;
delete window.vars.oldPosY;
delete window.vars.oldWidth;
delete window.vars.oldHeight;
wC.style.left = `${window.posX}px`;
wC.style.top = `${window.posY}px`;
wC.style.width = `${window.width + _windowPaddingX - 2}px`;
w.style.width = `${window.width}px`;
w.style.height = `${window.height}px`;
} else {
window.vars.oldPosX = window.posX;
window.vars.oldPosY = window.posY;
window.vars.oldWidth = window.width;
window.vars.oldHeight = window.height;
window.fullscreen = true;
window.posX = 0;
window.posY = 0;
window.width = innerWidth;
window.height = innerHeight;
wC.style.left = "0px";
wC.style.top = "0px";
wC.style.width = `${window.width}px`;
w.style.width = `${window.width - _windowPaddingX}px`;
w.style.height = `${window.height - _windowPaddingY}px`;
}
});
wH.querySelector(".fullscreen").addEventListener("click", (): void => (toggleFullscreen(config.id)));
wH.addEventListener("dblclick", (): void => (toggleFullscreen(config.id)));
globalIncrement++;
}

View file

@ -251,23 +251,30 @@ function _internal_neofetch(args: string, windowID: string): string {
return _internal_neofetchOutputs[args];
}
const _internal_commands: { [key: string]: (args: string, windowID: string) => string } = {
cat: cat,
cd: cd,
clear: clear,
help: help, // NEEDS UPDATING
ls: ls,
exit: exit,
_internal_set_ps1: _internal_set_ps1,
_internal_neofetch: _internal_neofetch
const _internal_commands: {
[key: string]: {
callback: (args: string, windowID: string) => string,
autocomplete: "dir" | "file" | null | string[]
}
} = {
cat: { callback: cat, autocomplete: "file" },
cd: { callback: cd, autocomplete: "dir" },
clear: { callback: clear, autocomplete: null },
help: { callback: help, autocomplete: null },
ls: { callback: ls, autocomplete: "file" },
exit: { callback: exit, autocomplete: null },
_internal_set_ps1: { callback: _internal_set_ps1, autocomplete: null },
_internal_neofetch: { callback: _internal_neofetch, autocomplete: null },
spin: { callback: (args: string, windowID: string): string => { createBlob(); return ""; }, autocomplete: null },
};
// -= Variables + Other =- //
const _internal_defaultFiles: StringDict = {
about: `<div><b>hi there! i'm trinkey!</b></div>
about: `<div><b>hi there!</b></div>
<div>i'm trinkey, but you can call me <b>katie</b> too! <small>(trinkey is more of a username)</small></div>
<div>--------------------</div>
<div>i'm a silly little kitty cat who lives in the usa (sadly).</div>
<div>i'm <span class="blue">t</span><span class="pink">r</span>a<span class="pink">n</span><span class="blue">s</span> (she/is, they/them is also fine).</div>
<div>i'm a silly little cat who lives in the usa (new york time zone).</div>
<div>i'm <span class="blue">t</span><span class="pink">r</span>a<span class="pink">n</span><span class="blue">s</span> (she/it, they/them is also fine).</div>
<div>i'm not actively in a relationship, however i'm also not looking to get into one either.</div>
<div>--------------------</div>
<div>i like to code stuff (mostly websites)! some of my programs can be found in projects section. i know a few languages, those being python, javascript/typescript, html/css (if you count those), and a little bit of java.</div>
@ -281,6 +288,7 @@ const _internal_defaultFiles: StringDict = {
<div>- signal - <b>@trinkey.01</b></div>
<div>- email - <b>trinkey [at] proton [dot] me</b></div>
<div>- youtube - <a href="https://youtube.com/@trinkey" target="_blank"><b>@trinkey</b></a> (inactive)</div>
<div>- discord - <b>@trinkey_</b> (mostly inactive)</div>
<div>- <a href="trinkey_gpg.asc" target="_blank">gpg key</a> (1D6E 5D28 BDD4 D7FA 96B8 8799 2B33 C6C6 14F2 591A)</div>`,
projects: `<div><b>projects</b> - the things i made</div>
<div>- <a href="https://github.com/jerimiah-smiggins/smiggins/" target="_blank"><b>smiggins</b></a> (<a href="https://smiggins.trinkey.com/" target="_blank">website</a>) - a social media platform i made</div>
@ -292,23 +300,28 @@ const _internal_defaultFiles: StringDict = {
buttons: `<div><b>my button:</b> (click to copy html)</div>
<div><img style="cursor: pointer;" src="img/88x31.png" alt="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." title="trinkey's 88x31. image of her cat on the right with the word 'trinkey' taking up the rest of the button." onclick="copyButton()"></div>
<div>--------------------</div>
<div><b>cool people:</b></div>
<div class="buttons-88x31">
<a href="https://notfire.cc" target="_blank"><img src="https://notfire.cc/design/images/buttons/notfire-cc-88x31-af.gif" alt="notfire.cc" title="notfire.cc"></a>
<a href="https://micro.niko.lgbt" target="_blank"><img src="https://micro.niko.lgbt/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;"></a>
<a href="https://microspinny.zip" target="_blank"><img src="https://microspinny.zip/static/button_2.png" alt="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;" title="a non-spinning demigirl blobcat angled slightly with a black border to the left of &quot;Micro&quot;"></a>
<a href="https://w.on-t.work" target="_blank"><img src="https://w.on-t.work/assets/88x31.png" alt="kopper's button" title="kopper's button"></a>
<a href="https://synth.download" target="_blank"><img src="https://synth.download/assets/buttons/sneexy.svg" alt="Sneexy" title="Sneexy"></a>
<a href="https://beepi.ng" target="_blank"><img src="https://beepi.ng/88x31.png" alt="unnick" title="unnick"></a>
<a href="http://autumn.town" target="_blank"><img src="https://autumn.town/assets/buttons/mybutton.webp" alt="Autumn Town Café" title="Autumn Town Café"></a>
<a href="https://redcatho.de" target="_blank"><img src="https://redcatho.de/buttons/red.png" alt="the text 'red is purple' on a purple background" title="the text 'red is purple' on a purple background"></a>
<a href="https://doskel.net" target="_blank"><img src="https://doskel.net/button.png" alt="doskel" title="doskel"></a>
<a href="https://velzie.rip" target="_blank"><img src="https://velzie.rip/88x31.png" alt="velzie" title="velzie"></a>
</div>`,
testimonials: `<div>"warning: this user is trinkey"</div>
<div>- <a href="https://booping.synth.download/@breaadyboy" target="_blank">bread</a></div><br>
<div>"This user is only slightly crazy once was I. 10/10 would recommend"</div>
<div>- <a href="https://lea.pet/@subroutine" target="_blank">subroutine</a></div><br>
<div>"the f slur but repeated 36 times"</div>
<div>- <a href="https://oomfie.city/@cornfields74">corn fields seventy four</a></div>`,
<div>- <a href="https://oomfie.city/@cornfields74" target="_blank">corn fields seventy four</a></div><br>
<div>"silly cute kitn"</div>
<div>- <a href="https://microspinny.zip" target="_blank">niko</a></div><br>
<div>"very bitable <img class="emoji" alt="neodog_bite_neocat" src="img/emoji/neodog_bite_neocat.png"></div>
<div>- <a href="https://booping.synth.download/@strongsand" target="_blank">strongsand</a></div>
`,
webrings: `<div>
<a href="https://ctp-webr.ing/trinkey/previous">&larr;</a>
<a href="https://ctp-webr.ing/">catppuccin webring</a>
@ -346,6 +359,7 @@ const _internal_defaultFiles: StringDict = {
<div>the <a href="https://codeberg.org/KittyShopper/mastoapi-fe">outpost</a> frontend for fedi made by kopper</div><br>
<div><b><a href="https://smiggins.trinkey.com/">smiggins.trinkey.com</a>:</b></div>
<div>official jerimiah smiggins instance, that being my own social media platform</div><br>
<div>there's also a qna for me at *<a href="https://trinkey.com/qna/index.php">trinkey.com/qna</a></div><br>
<div>(asterisk (*) means i haven't written the code for it)</div><br>`
};
@ -390,25 +404,28 @@ const _internal_neofetchOutputs: StringDict = {
<b>\`\`-:::::-\`\`</b></pre>`
};
const helpText: string = `<div>&nbsp;-=== <b class="pink">tSh help</b> ===-</div>
const helpText: string = `
<div>&nbsp;-=== <b class="pink">tSh help</b> ===-</div>
<div>--------------------</div>
<div>-= <b class="green">cat</b> =-</div>
<div>Displays the contents of a file.</div>
<div>displays the contents of a file</div>
<div>-= <b class="green">cd</b> =-</div>
<div>Changes the working directory.</div>
<div>changes the working directory</div>
<div>-= <b class="green">clear</b> =-</div>
<div>Clears the terminal output.</div>
<div>clears the terminal output</div>
<div>-= <b class="green">help</b> =-</div>
<div>Shows this help menu.</div>
<div>shows this help menu</div>
<div>-= <b class="green">ls</b> =-</div>
<div>Lists all files in a directory.</div>
<div>lists all files in a directory</div>
<div>&nbsp; -l - displays more information about each file</div>
<div>&nbsp; -a - displays all files</div>
<div>&nbsp; -A - displays all files except implied . and ..</div>
<div>&nbsp; -r - reverses the order of the files</div>
<div>&nbsp; -R - recurse through all subdirectories</div>
<div>-= <b class="green">spin</b> =-</div>
<div>creates a new window with a spinning blobcat wireframe</div>
<div>-= <b class="green">exit</b> =-</div>
<div>Closes the terminal.</div>`;
<div>closes the terminal window</div>`;
const HOME_DIR: string = "/home/trinkey";
@ -442,8 +459,7 @@ let FILESYSTEM: _files = {
cd: { type: "file", name: "cd", content: "<div>function cd(directory: string): void { ... }</div>" },
clear: { type: "file", name: "clear", content: "<div>function clear(): void { ... }</div>" },
help: { type: "file", name: "help", content: "<div>function help(): string { ... }</div>" },
ls: { type: "file", name: "ls", content: "<div>function ls(directory: string): string { ... }</div>" },
neofetch: { type: "file", name: "neofetch", content: "<div>function neofetch(): string { ... }</div>" }
ls: { type: "file", name: "ls", content: "<div>function ls(directory?: string): string { ... }</div>" }
}
},
".secret-file": { type: "file", name: ".secret-file", content: "<div>meow :3</div>" }
@ -452,23 +468,21 @@ let FILESYSTEM: _files = {
let windowInformation: { [key: string]: _tShWinInfo } = {};
function commandManager(windowID: string, command: string): void {
if (!windowInformation[windowID]) {
windowInformation[windowID] = {
PWD: HOME_DIR
};
}
let out: string;
if (_internal_commands[command.split(" ")[0]]) {
out = _internal_commands[command.split(" ")[0]](command.split(" ").slice(1).join(" ").trim(), windowID);
out = _internal_commands[command.split(" ")[0]].callback(command.split(" ").slice(1).join(" ").trim(), windowID);
} else if (command == "") {
out = ""
} else {
out = `<div class="red">Unknown command '${escapeHTML(command.split(" ")[0])}'.</div><div>Type 'help' for a list of commands</div>`;
}
addWindowCommand(windowID, out);
}
function addWindowCommand(windowID: string, value: string): void {
let el: HTMLDivElement = document.createElement("div");
el.innerHTML = out;
el.innerHTML = value;
WINDOWS[windowID].element.querySelector(".window").append(el);
let dTE: HTMLElement = WINDOWS[windowID].element.querySelector("[data-type-area]");