1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/console/templates/console-xterm.html
catborise abc6f2063d fix(console): console pages render in standards mode and fit the window
console-base.html had no doctype, so the noVNC and serial console pages
rendered in quirks mode, where body and #main_container got their height
from the viewport. The console area overflowed the window: the full noVNC
page scrolled by the navbar height, the lite page's canvas and the serial
terminal ran below the bottom edge, hiding the last rows.

With the doctype, #main_container is a viewport-high flex column and the
console area (the noVNC container or the terminal) takes the space below
the navbar. Without that, the lite page's canvas collapsed to 0x0.
2026-10-19 09:31:37 +03:00

103 lines
3.5 KiB
HTML

{% extends "console-base.html" %}
{% load i18n %}
{% load static %}
{% block head %}
<title>WebVirtCloud - XTerm</title>
<link rel="stylesheet" href="{% static 'css/xterm.css' %}"/>
<script src="{% static 'js/xterm/xterm.js' %}"></script>
<script src="{% static 'js/xterm/addon-fit.js' %}"></script>
<script src="{% static 'js/socket.io.js' %}"></script>
<style>
#terminal { flex: 1; min-height: 0; }
</style>
{% endblock %}
{% block content %}
<div style="background: white; padding-bottom: 5px;">
<span style="font-size: small;">Status: <span style="font-size: small;" id="status">connecting...</span></span>
<button id="button" type="button" onclick="myFunction();">Connect</button>
</div>
<div id="terminal"></div>
<script>
// socketiod is behind this page's own /socket.io/ location; the page's
// signed token is the credential (the cookie holds the last console's)
var socket = io({transports: ["websocket", "polling"], auth: {token: "{{ token|escapejs }}"}});
const status = document.getElementById("status");
const button = document.getElementById("button");
var term = new Terminal({
cursorBlink: true,
});
var fitAddon = new FitAddon.FitAddon();
term.loadAddon(fitAddon);
term.open(document.getElementById('terminal'));
// typed and pasted input
term.onData((data) => {
socket.emit("pty_input", {"input": data})
});
socket.on("pty_output", function(output){
term.write(output["output"])
})
function showStatus(text, color) {
status.textContent = text;
status.style.backgroundColor = color;
button.textContent = socket.connected ? "Disconnect" : "Connect";
}
socket.on("connect", () => showStatus("connected", "lightgreen"));
socket.on("disconnect", () => showStatus("disconnected", "#ff8383"));
// refused: serial console off, an expired token, or the VM's console already open
socket.on("connect_error", () => showStatus("refused", "#ff8383"));
function myFunction(){
if (socket.connected) {
socket.disconnect();
} else {
location.reload();
}
}
function resize(){
fitAddon.fit()
socket.emit("resize", {"cols": term.cols, "rows": term.rows})
}
function bindTerminalKey(id, str) {
const el = document.getElementById(id);
if (el) {
el.addEventListener('click', (e) => {
e.preventDefault();
socket.emit("pty_input", {"input": str});
});
}
}
bindTerminalKey('ctrlc', '\x03');
bindTerminalKey('ctrld', '\x04');
bindTerminalKey('ctrlz', '\x1a');
bindTerminalKey('ctrlbackslash', '\x1c');
bindTerminalKey('sendtab', '\t');
bindTerminalKey('sendesc', '\x1b');
const fsBtn = document.getElementById('fullscreen_button');
if (fsBtn) {
fsBtn.addEventListener('click', (e) => {
e.preventDefault();
if (document.fullscreenElement) {
document.exitFullscreen().catch(() => {});
} else {
document.documentElement.requestFullscreen().catch(() => {});
}
});
}
window.onresize = resize;
window.onload = resize;
</script>
{% endblock %}