document.addEventListener("DOMContentLoaded", function () { const input = document.getElementById("command-input"); // We assume your normal terminal UI is in the element with id "terminal". const normalTerminal = document.getElementById("terminal"); let interactiveWS = null; let interactiveMode = false; const ansi_up = new AnsiUp; input.addEventListener("keydown", function (event) { if (!interactiveMode && event.key === "Enter") { const command = input.value.trim(); if (command === "start-interactive") { startInteractiveSession(); input.value = ""; event.preventDefault(); return; } // Otherwise, your normal HTTP submission… } }); function startInteractiveSession() { interactiveMode = true; // Hide the normal terminal and input. normalTerminal.style.display = "none"; input.style.display = "none"; // Create a new container for xterm.js. const xtermContainer = document.createElement("div"); xtermContainer.id = "xterm-container"; xtermContainer.style.position = "fixed"; xtermContainer.style.top = "0"; xtermContainer.style.left = "0"; xtermContainer.style.width = window.innerWidth + "px"; xtermContainer.style.height = window.innerHeight + "px"; xtermContainer.style.zIndex = "1000"; document.body.appendChild(xtermContainer); const term = new Terminal({ cursorBlink: true, cursorStyle: 'block', scrollback: 1000, fontSize: 18, theme: { background: "#222", foreground: "#eee" } }); const fitAddon = new FitAddon.FitAddon(); term.loadAddon(fitAddon); term.open(xtermContainer); setTimeout(() => { fitAddon.fit(); term.focus(); console.log("Initial fit: container width =", xtermContainer.offsetWidth, "cols =", term.cols); }, 100); interactiveWS = new WebSocket("ws://" + location.host + "/terminal"); interactiveWS.binaryType = "arraybuffer"; interactiveWS.onopen = function () { sendResize(); }; interactiveWS.onmessage = function (event) { const text = new TextDecoder("utf-8").decode(event.data); term.write(text); }; interactiveWS.onclose = function () { interactiveMode = false; term.write("\r\n--- Interactive session ended ---\r\n"); if (xtermContainer.parentNode) { xtermContainer.parentNode.removeChild(xtermContainer); } window.removeEventListener("resize", handleResize); normalTerminal.style.display = "block"; input.style.display = "block"; input.focus(); }; interactiveWS.onerror = function (err) { term.write("\r\n--- Error in interactive session ---\r\n"); console.error("Interactive WS error:", err); interactiveMode = false; if (xtermContainer.parentNode) { xtermContainer.parentNode.removeChild(xtermContainer); } window.removeEventListener("resize", handleResize); normalTerminal.style.display = "block"; input.style.display = "block"; }; term.onData(function (data) { if (data === "\x04") { term.write("\r\n--- Exiting interactive session ---\r\n"); interactiveWS.close(); } else { interactiveWS.send(data); } }); function handleResize() { const newWidth = window.innerWidth; const newHeight = window.innerHeight; xtermContainer.style.width = newWidth + "px"; xtermContainer.style.height = newHeight + "px"; console.log("Resizing: new width =", newWidth, "new height =", newHeight); fitAddon.fit(); sendResize(); } window.addEventListener("resize", handleResize); // Send a resize message using a custom control prefix (0xFF). function sendResize() { const resizeData = { type: "resize", cols: term.cols, rows: term.rows }; const jsonStr = JSON.stringify(resizeData); const encoder = new TextEncoder(); const jsonBuffer = encoder.encode(jsonStr); // Create a Uint8Array with one extra byte for the prefix. const buffer = new Uint8Array(jsonBuffer.length + 1); buffer[0] = 0xFF; // Control prefix. buffer.set(jsonBuffer, 1); interactiveWS.send(buffer.buffer); console.log("Sent resize: cols =", term.cols, "rows =", term.rows); } } });