//// // The scrollback shows your history of interacting with the shell. // input, output, etc import { scrollback, $$ } from "./dom.js" import { randomId } from "../shared/utils.js" import type { CommandOutput } from "../shared/types.js" type InputStatus = "waiting" | "streaming" | "ok" | "error" export function autoScroll() { // requestAnimationFrame(() => scrollback.scrollTop = scrollback.scrollHeight - scrollback.clientHeight) // scrollback.scrollTop = scrollback.scrollHeight - scrollback.clientHeight } export function insert(node: HTMLElement) { scrollback.append(node) } export function addInput(id: string, input: string) { const parent = $$("li.input") const status = $$("span.status.yellow", "•") const content = $$("span.content", input) parent.append(status, content) parent.dataset.id = id insert(parent) scrollback.scrollTop = scrollback.scrollHeight - scrollback.clientHeight } export function setStatus(id: string, status: InputStatus) { const statusEl = document.querySelector(`[data-id="${id}"].input .status`) if (!statusEl) return statusEl.className = "" switch (status) { case "waiting": statusEl.classList.add("yellow") break case "streaming": statusEl.classList.add("purple") break case "ok": statusEl.classList.add("green") break case "error": statusEl.classList.add("red") break } } export function addOutput(id: string, output: CommandOutput) { const item = $$("li") item.classList.add("output") item.dataset.id = id || randomId() const [format, content] = processOutput(output) if (format === "html") item.innerHTML = content else item.textContent = content const input = document.querySelector(`[data-id="${id}"].input`) if (input instanceof HTMLLIElement) input.parentNode!.insertBefore(item, input.nextSibling) else insert(item) autoScroll() } export function addErrorMessage(message: string) { addOutput("", { html: `${message}` }) } export function appendOutput(id: string, output: CommandOutput) { const item = document.querySelector(`[data-id="${id}"].output`) if (!item) { console.error(`output id ${id} not found`) return } const [format, content] = processOutput(output) if (format === "html") item.innerHTML += content else item.textContent += content autoScroll() } export function replaceOutput(id: string, output: CommandOutput) { const item = document.querySelector(`[data-id="${id}"].output`) if (!item) { console.error(`output id ${id} not found`) return } const [format, content] = processOutput(output) if (format === "html") item.innerHTML = content else item.textContent = content autoScroll() } function processOutput(output: CommandOutput): ["html" | "text", string] { let content = "" let html = false if (typeof output === "string") { content = output } else if (Array.isArray(output)) { content = output.join(" ") } else if ("html" in output) { html = true content = output.html if (output.script) eval(output.script) } else if ("text" in output) { content = output.text if (output.script) eval(output.script) } else if ("script" in output) { eval(output.script!) } else { content = JSON.stringify(output) } return [html ? "html" : "text", content] }