gommand/static/start-interactive.js

133 lines
4.1 KiB
JavaScript

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);
}
}
});