mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
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.
103 lines
3.5 KiB
HTML
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 %}
|