159 lines
5.0 KiB
JavaScript
159 lines
5.0 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…
|
||
}
|
||
});
|
||
|
||
// Helper: get ?ip=…&port=… from the current location
|
||
function getQueryParam(name) {
|
||
return new URLSearchParams(window.location.search).get(name);
|
||
}
|
||
|
||
function makeWsUrl() {
|
||
const proxyIp = getQueryParam("ip"); // "10.0.0.42" if you came via /proxyAgent
|
||
const proxyPort = getQueryParam("port"); // "8080"
|
||
const usingProxy = proxyIp && proxyPort; // truthy only in that case
|
||
|
||
if (usingProxy) {
|
||
// Build ws(s)://<main-server>/proxyAgent/terminal?ip=…&port=…
|
||
const u = new URL("/proxyAgent/terminal", window.location);
|
||
u.searchParams.set("ip", proxyIp);
|
||
u.searchParams.set("port", proxyPort);
|
||
u.protocol = u.protocol === "https:" ? "wss:" : "ws:";
|
||
return u.toString();
|
||
}
|
||
// Fallback: open directly on the agent we’re already on
|
||
const u = new URL("/terminal", window.location);
|
||
u.protocol = u.protocol === "https:" ? "wss:" : "ws:";
|
||
return u.toString();
|
||
}
|
||
|
||
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(makeWsUrl());
|
||
// 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);
|
||
}
|
||
}
|
||
});
|