train / pyxtermjs /index.html
ar08's picture
Update pyxtermjs/index.html
8acffa8 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Enhanced Terminal</title>
<link rel="stylesheet" href="https://unpkg.com/xterm@4.11.0/css/xterm.css" />
<style>
html {
font-family: Arial, sans-serif;
}
body {
background-color: #1e1e1e; /* Dark grey background */
color: white;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
height: 100vh;
}
#status-bar {
background-color: #333; /* Darker background for status bar */
color: #fff;
padding: 5px 10px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: small;
}
#terminal-container {
flex: 1;
display: flex;
flex-direction: column;
padding: 10px;
}
#terminal {
flex: 1;
width: 100%;
height: 100%;
border: 1px solid #444; /* Border around terminal */
border-radius: 5px; /* Rounded corners */
overflow: hidden; /* Hide overflow */
}
/* Custom scrollbar styles */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track {
background: #2e2e2e; /* Darker background for track */
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #555; /* Grey color for thumb */
border-radius: 10px;
border: 3px solid #2e2e2e; /* Padding around thumb */
}
::-webkit-scrollbar-thumb:hover {
background-color: #777; /* Lighter grey on hover */
}
.link-container {
text-align: right;
font-size: small;
margin-top: 5px;
}
.link-container a {
color: #1e90ff; /* Link color */
text-decoration: none;
}
.link-container a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div id="status-bar">
<span>Status: <span id="status">connecting...</span></span>
<span id="resize-status"></span>
</div>
<div id="terminal-container">
<div id="terminal"></div>
<div class="link-container">
<a href="https://www.youtube.com/watch?v=o-YBDTqX_ZU">Get Rickrolled</a> |
<a href="https://github.com">GitHub</a>
</div>
</div>
<script src="https://unpkg.com/xterm@4.11.0/lib/xterm.js"></script>
<script src="https://unpkg.com/xterm-addon-fit@0.5.0/lib/xterm-addon-fit.js"></script>
<script src="https://unpkg.com/xterm-addon-web-links@0.4.0/lib/xterm-addon-web-links.js"></script>
<script src="https://unpkg.com/xterm-addon-search@0.8.0/lib/xterm-addon-search.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
<script>
const term = new Terminal({
cursorBlink: true,
macOptionIsMeta: true,
scrollback: true,
});
const fit = new FitAddon.FitAddon();
term.loadAddon(fit);
term.loadAddon(new WebLinksAddon.WebLinksAddon());
term.loadAddon(new SearchAddon.SearchAddon());
term.open(document.getElementById("terminal"));
fit.fit();
term.resize(15, 50);
console.log(`size: ${term.cols} columns, ${term.rows} rows`);
fit.fit();
term.writeln("Welcome to AR-server");
term.writeln("");
term.writeln("You can copy with ctrl+shift+x");
term.writeln("You can paste with ctrl+shift+v");
term.writeln('');
term.onData((data) => {
console.log("browser terminal received new data:", data);
socket.emit("pty-input", { input: data });
});
const socket = io.connect("/pty");
const status = document.getElementById("status");
socket.on("pty-output", function (data) {
console.log("new output received from server:", data.output);
term.write(data.output);
});
socket.on("connect", () => {
fitToscreen();
status.innerHTML = '<span style="background-color: lightgreen; padding: 2px 4px; border-radius: 3px;">connected</span>';
});
socket.on("disconnect", () => {
status.innerHTML = '<span style="background-color: #ff8383; padding: 2px 4px; border-radius: 3px;">disconnected</span>';
});
function fitToscreen() {
fit.fit();
const dims = { cols: term.cols, rows: term.rows };
console.log("sending new dimensions to server's pty", dims);
socket.emit("resize", dims);
document.getElementById("resize-status").textContent = `Resized to: ${dims.cols}x${dims.rows}`;
}
function debounce(func, wait_ms) {
let timeout;
return function (...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait_ms);
};
}
/**
* Handle copy and paste events
*/
function customKeyEventHandler(e) {
if (e.type !== "keydown") {
return true;
}
if (e.ctrlKey && e.shiftKey) {
const key = e.key.toLowerCase();
if (key === "v") {
// ctrl+shift+v: paste whatever is in the clipboard
navigator.clipboard.readText().then((toPaste) => {
socket.emit("pty-input", { input: toPaste });
});
// Prevent default paste action
e.preventDefault();
return false;
} else if (key === "c" || key === "x") {
// ctrl+shift+x: copy whatever is highlighted to clipboard
const toCopy = term.getSelection();
if (toCopy) {
navigator.clipboard.writeText(toCopy).then(() => {
term.focus();
});
}
return false;
}
}
return true;
}
const wait_ms = 50;
window.onresize = debounce(fitToscreen, wait_ms);
term.attachCustomKeyEventHandler(customKeyEventHandler);
</script>
</body>
</html>