2025-02-11 15:11:22 +01:00
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
|
|
const input = document.getElementById("command-input");
|
2025-02-12 17:22:59 +01:00
|
|
|
// We assume your normal terminal UI is in the element with id "terminal".
|
|
|
|
const normalTerminal = document.getElementById("terminal");
|
2025-02-11 15:11:22 +01:00
|
|
|
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;
|
|
|
|
}
|
2025-02-12 17:22:59 +01:00
|
|
|
// Otherwise, your normal HTTP submission…
|
2025-02-11 15:11:22 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
function startInteractiveSession() {
|
2025-02-13 16:45:00 +01:00
|
|
|
interactiveMode = true;
|
|
|
|
// Hide the normal terminal and input.
|
|
|
|
normalTerminal.style.display = "none";
|
|
|
|
input.style.display = "none";
|
2025-02-12 17:22:59 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
// 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);
|
2025-02-11 15:11:22 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
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(() => {
|
2025-02-12 17:22:59 +01:00
|
|
|
fitAddon.fit();
|
|
|
|
term.focus();
|
2025-02-13 16:45:00 +01:00
|
|
|
console.log("Initial fit: container width =", xtermContainer.offsetWidth, "cols =", term.cols);
|
|
|
|
}, 100);
|
2025-02-12 17:22:59 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
interactiveWS = new WebSocket("ws://" + location.host + "/terminal");
|
|
|
|
interactiveWS.binaryType = "arraybuffer";
|
2025-02-11 15:11:22 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
interactiveWS.onopen = function () {
|
|
|
|
sendResize();
|
|
|
|
};
|
2025-02-11 15:11:22 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
interactiveWS.onmessage = function (event) {
|
|
|
|
const text = new TextDecoder("utf-8").decode(event.data);
|
|
|
|
term.write(text);
|
|
|
|
};
|
2025-02-11 15:11:22 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
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();
|
|
|
|
};
|
2025-02-11 15:11:22 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
});
|
2025-02-11 15:11:22 +01:00
|
|
|
|
2025-02-13 16:45:00 +01:00
|
|
|
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);
|
2025-02-11 15:11:22 +01:00
|
|
|
}
|
2025-02-13 16:45:00 +01:00
|
|
|
}
|
2025-02-11 15:11:22 +01:00
|
|
|
});
|