From 332051138391a86325e4e780895aa4148f81c5d5 Mon Sep 17 00:00:00 2001 From: Kyren223 Date: Sat, 10 Oct 2026 04:43:44 +0300 Subject: Website stuff, mostly done, needs finishing touches that I am too lazy to do --- public/shell.js | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 public/shell.js (limited to 'public/shell.js') diff --git a/public/shell.js b/public/shell.js new file mode 100644 index 0000000..9a70846 --- /dev/null +++ b/public/shell.js @@ -0,0 +1,73 @@ +const shell = document.querySelector("[data-shell]"); + +if (shell) { + const area = shell.closest("[data-scroll-area]"); + const history = shell.querySelector("#shell-history"); + const input = shell.querySelector("[data-shell-input]"); + const caret = shell.querySelector("[data-shell-caret]"); + let busy = false; + + const updateCaret = () => { + const pos = input.selectionStart ?? input.value.length; + caret.style.left = `calc(${pos}ch - ${input.scrollLeft}px)`; + caret.textContent = input.value[pos] ?? "\u00a0"; + caret.style.animation = "none"; // restart the blink so the caret is solid while typing + void caret.offsetWidth; + caret.style.animation = ""; + }; + + const snapToBottom = () => { + area.scrollTop = area.scrollHeight; + }; + + const run = async (cmd) => { + busy = true; + try { + const res = await fetch("/shell/run", { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ cmd }), + }); + const data = await res.json(); + if (data.clear) history.replaceChildren(); + if (data.html) history.insertAdjacentHTML("beforeend", data.html); + if (data.vars) { + for (const [key, value] of Object.entries(data.vars)) { + document.documentElement.style.setProperty(`--${key}`, value); + } + } + } catch { + const el = document.createElement("p"); + el.textContent = "request failed"; + history.append(el); + } finally { + busy = false; + snapToBottom(); + } + }; + + input.addEventListener("keydown", (e) => { + if (e.key !== "Enter") return; + e.preventDefault(); + const cmd = input.value.trim(); + if (!cmd || busy) return; + input.value = ""; + updateCaret(); + run(cmd); + }); + + for (const event of ["input", "keyup", "click", "focus"]) { + input.addEventListener(event, updateCaret); + } + document.addEventListener("selectionchange", () => { + if (document.activeElement === input) updateCaret(); + }); + + area.addEventListener("click", () => { + if (!getSelection().toString()) input.focus({ preventScroll: true }); + }); + + input.focus({ preventScroll: true }); + updateCaret(); + snapToBottom(); +} -- cgit v1.3.1