diff --git a/public/bundle.js b/public/bundle.js index 05c35b4..18b242e 100644 --- a/public/bundle.js +++ b/public/bundle.js @@ -1,7 +1,8 @@ //// -// version: 5933c50 +// version: c1faf0e // src/js/dom.ts +var content2 = $("content"); var cmdLine = $("command-line"); var cmdInput = $("command-textbox"); var scrollback = $("scrollback"); @@ -24,7 +25,7 @@ var $$ = (tag, innerHTML = "") => { }; // src/js/resize.ts -var content2 = document.getElementById("content"); +var content3 = document.getElementById("content"); function initResize() { window.addEventListener("resize", resize); resize(); @@ -38,13 +39,13 @@ function resize() { } function resizeTall() { const scale = Math.min(1, window.innerWidth / 960); - content2.style.transformOrigin = "top center"; - content2.style.transform = `scaleX(${scale})`; + content3.style.transformOrigin = "top center"; + content3.style.transform = `scaleX(${scale})`; } function resizeCinema() { const scale = Math.min(window.innerWidth / 960, window.innerHeight / 540); - content2.style.transformOrigin = "center center"; - content2.style.transform = `scale(${scale})`; + content3.style.transformOrigin = "center center"; + content3.style.transform = `scale(${scale})`; } // src/shared/utils.ts @@ -63,8 +64,8 @@ function insert(node) { function addInput(id, input) { const parent = $$("li.input"); const status = $$("span.status.yellow", "•"); - const content3 = $$("span.content", input); - parent.append(status, content3); + const content4 = $$("span.content", input); + parent.append(status, content4); parent.dataset.id = id; insert(parent); scrollback.scrollTop = scrollback.scrollHeight - scrollback.clientHeight; @@ -86,11 +87,11 @@ function addOutput(id, output2) { const item = $$("li"); item.classList.add("output"); item.dataset.id = id || randomId(); - const [format, content3] = processOutput(output2); + const [format, content4] = processOutput(output2); if (format === "html") - item.innerHTML = content3; + item.innerHTML = content4; else - item.textContent = content3; + item.textContent = content4; const input = document.querySelector(`[data-id="${id}"].input`); if (input instanceof HTMLLIElement) { input.parentNode.insertBefore(item, input.nextSibling); @@ -108,11 +109,11 @@ function appendOutput(id, output2) { console.error(`output id ${id} not found`); return; } - const [format, content3] = processOutput(output2); + const [format, content4] = processOutput(output2); if (format === "html") - item.innerHTML += content3; + item.innerHTML += content4; else - item.textContent += content3; + item.textContent += content4; autoScroll(); } function replaceOutput(id, output2) { @@ -121,11 +122,11 @@ function replaceOutput(id, output2) { console.error(`output id ${id} not found`); return; } - const [format, content3] = processOutput(output2); + const [format, content4] = processOutput(output2); if (format === "html") - item.innerHTML = content3; + item.innerHTML = content4; else - item.textContent = content3; + item.textContent = content4; autoScroll(); } function processOutput(output) { @@ -159,150 +160,32 @@ function handleInputClick(e) { cmdInput.value = target.textContent; } } +function handleOutput(msg) { + const result = msg.data; + setStatus(msg.id, result.status); + addOutput(msg.id, result.output); +} // src/js/session.ts var sessionId = randomId(); -// src/js/commands.ts -var commands = []; -var browserCommands = { - "browser-session": () => sessionId, - clear: () => scrollback.innerHTML = "", - commands: () => commands.join(" "), - fullscreen: () => document.body.requestFullscreen(), - mode: () => { - document.body.dataset.mode = document.body.dataset.mode === "tall" ? "cinema" : "tall"; - resize(); - autoScroll(); - }, - reload: () => window.location.reload() -}; -function cacheCommands(cmds) { - commands.length = 0; - commands.push(...cmds); - commands.push(...Object.keys(browserCommands)); - commands.sort(); -} - -// src/js/completion.ts -function initCompletion() { - cmdInput.addEventListener("keydown", handleCompletion); -} -function handleCompletion(e) { - if (e.key !== "Tab") +// src/js/stream.ts +function handleStreamStart(msg) { + const id = msg.id; + const status = document.querySelector(`[data-id="${id}"].input .status`); + if (!status) return; - e.preventDefault(); - const input = cmdInput.value; - for (const command of commands) { - if (command.startsWith(input)) { - cmdInput.value = command; - return; - } - } + addOutput(id, msg.data); + status.classList.remove("yellow"); + status.classList.add("purple"); } - -// src/js/cursor.ts -var cursor = "Û"; -var cmdCursor; -var enabled = true; -function initCursor() { - cmdCursor = $("command-cursor"); - cmdInput.addEventListener("keydown", showCursor); - document.addEventListener("focus", cursorEnablerHandler, true); - showCursor(); +function handleStreamAppend(msg) { + appendOutput(msg.id, msg.data); } -function showCursor(e = {}) { - if (!enabled) { - cmdCursor.value = ""; - return; - } - if (e.key === "Enter" && !e.shiftKey) { - cmdCursor.value = cursor; - return; - } - requestAnimationFrame(() => cmdCursor.value = buildBlankCursorLine() + cursor); -} -function cursorEnablerHandler(e) { - if (!e.target) - return; - const target = e.target; - enabled = target.id === "command-textbox"; - showCursor(); -} -function buildBlankCursorLine() { - let line = ""; - for (const char of cmdInput.value.slice(0, cmdInput.selectionEnd)) { - line += char === ` -` ? char : " "; - } - return line; -} - -// src/js/drop.ts -function initDrop() { - ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { - document.body.addEventListener(eventName, preventDefaults, false); - }); - document.body.addEventListener("drop", handleDrop); -} -function preventDefaults(e) { - e.preventDefault(); - e.stopPropagation(); -} -function handleDrop(e) { - const fileInput = document.querySelector("input[type=file]"); - const files = e.dataTransfer?.files ?? []; - if (files.length > 0) { - const dt = new DataTransfer; - Array.from(files).forEach((f) => dt.items.add(f)); - fileInput.files = dt.files; - fileInput.dispatchEvent(new Event("change", { bubbles: true })); - } -} - -// src/js/history.ts -var history = ["one", "two", "three"]; -var idx = -1; -var savedInput = ""; -function initHistory() { - cmdInput.addEventListener("keydown", navigateHistory); -} -function addToHistory(input) { - if (history.length === 0 || history[0] === input) - return; - history.unshift(input); - resetHistory(); -} -function resetHistory() { - idx = -1; - savedInput = ""; -} -function navigateHistory(e) { - if (cmdLine.dataset.extended) - return; - if (e.key === "ArrowUp" || e.ctrlKey && e.key === "p") { - e.preventDefault(); - if (idx >= history.length - 1) - return; - if (idx === -1) - savedInput = cmdInput.value; - cmdInput.value = history[++idx] || ""; - if (idx >= history.length) - idx = history.length - 1; - } else if (e.key === "ArrowDown" || e.ctrlKey && e.key === "n") { - e.preventDefault(); - if (idx <= 0) { - cmdInput.value = savedInput; - idx = -1; - return; - } - cmdInput.value = history[--idx] || ""; - if (idx < -1) - idx = -1; - } else if (idx !== -1) { - resetHistory(); - } +function handleStreamReplace(msg) { + replaceOutput(msg.id, msg.data); } +function handleStreamEnd(_msg) {} // src/shared/game.ts class GameContext { @@ -624,28 +507,8 @@ function endGame() { focusInput(); } -// src/js/shell.ts -function runCommand(input) { - if (!input.trim()) - return; - if (input.includes(";")) { - input.split(";").forEach((cmd2) => runCommand(cmd2.trim())); - return; - } - const id = randomId(); - addToHistory(input); - addInput(id, input); - const [cmd = "", ..._args] = input.split(" "); - if (browserCommands[cmd]) { - const result = browserCommands[cmd](); - if (typeof result === "string") - addOutput(id, result); - setStatus(id, "ok"); - } else { - send({ id, type: "input", data: input }); - } -} -async function handleMessage(msg) { +// src/js/dispatch.ts +async function dispatchMessage(msg) { switch (msg.type) { case "output": handleOutput(msg); @@ -671,31 +534,13 @@ async function handleMessage(msg) { case "game:start": await handleGameStart(msg); break; + case "ui:mode": + browserCommands.mode?.(msg.data); + break; default: console.error("unknown message type", msg); } } -function handleOutput(msg) { - const result = msg.data; - setStatus(msg.id, result.status); - addOutput(msg.id, result.output); -} -function handleStreamStart(msg) { - const id = msg.id; - const status = document.querySelector(`[data-id="${id}"].input .status`); - if (!status) - return; - addOutput(id, msg.data); - status.classList.remove("yellow"); - status.classList.add("purple"); -} -function handleStreamAppend(msg) { - appendOutput(msg.id, msg.data); -} -function handleStreamReplace(msg) { - replaceOutput(msg.id, msg.data); -} -function handleStreamEnd(_msg) {} // src/js/websocket.ts var MAX_RETRIES = 5; @@ -704,7 +549,7 @@ var connected = false; var msgQueue = []; var ws = null; function startConnection() { - const url = new URL("/ws", location.href); + const url = new URL(`/ws?session=${sessionId}`, location.href); url.protocol = url.protocol.replace("http", "ws"); ws = new WebSocket(url); ws.onmessage = receive; @@ -730,7 +575,7 @@ function send(msg) { async function receive(e) { const data = JSON.parse(e.data); console.log("<- receive", data); - await handleMessage(data); + await dispatchMessage(data); } function retryConnection() { connected = false; @@ -745,6 +590,112 @@ function retryConnection() { setTimeout(startConnection, 2000); } +// src/js/commands.ts +var commands = []; +var browserCommands = { + "browser-session": () => sessionId, + clear: () => scrollback.innerHTML = "", + commands: () => { + return { html: "