diff options
Diffstat (limited to 'public/shell.js')
| -rw-r--r-- | public/shell.js | 128 |
1 files changed, 65 insertions, 63 deletions
diff --git a/public/shell.js b/public/shell.js index 9a70846..3f3aef8 100644 --- a/public/shell.js +++ b/public/shell.js @@ -1,73 +1,75 @@ -const shell = document.querySelector("[data-shell]"); +document.addEventListener("htmx:load", () => { + 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; + 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 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 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); + 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(); } - } 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); - }); + 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(); - }); + 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 }); - }); + area.addEventListener("click", () => { + if (!getSelection().toString()) input.focus({ preventScroll: true }); + }); - input.focus({ preventScroll: true }); - updateCaret(); - snapToBottom(); -} + input.focus({ preventScroll: true }); + updateCaret(); + snapToBottom(); + } +}); |
