Files
wterm/static/index.html
T
2025-11-17 16:41:34 +07:00

176 lines
6.1 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Web Terminal</title>
<link rel="stylesheet" href="xterm.css">
<style>
body {
margin: 0;
padding: 20px;
background-color: #1e1e1e;
color: #fff;
font-family: Arial, sans-serif;
height: 100vh;
display: flex;
flex-direction: column;
}
#terminal-container {
flex: 1;
border: 1px solid #333;
border-radius: 5px;
overflow: hidden;
margin-top: 10px;
}
.status {
padding: 10px;
border-radius: 3px;
font-weight: bold;
margin-bottom: 10px;
}
.connected { background-color: #2d5a2d; }
.disconnected { background-color: #5a2d2d; }
.connecting { background-color: #5a5a2d; }
</style>
</head>
<body>
<h1>Web Terminal</h1>
<div class="status connecting" id="status">Connecting...</div>
<div id="terminal-container"></div>
<script src="xterm.js"></script>
<script src="xterm-addon-fit.js"></script>
<script>
class WebTerminal {
constructor() {
this.terminal = new Terminal({
cursorBlink: true,
theme: {
background: '#000000',
foreground: '#ffffff',
cursor: '#ffffff'
}
});
this.fitAddon = new FitAddon.FitAddon();
this.terminal.loadAddon(this.fitAddon);
this.ws = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 3000;
this.init();
}
init() {
const terminalContainer = document.getElementById('terminal-container');
this.terminal.open(terminalContainer);
this.fitAddon.fit();
this.terminal.writeln('Welcome to Web Terminal');
this.terminal.writeln('Connecting to server...');
this.connect();
// Handle terminal input
this.terminal.onData((data) => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data);
}
});
// Handle resize
window.addEventListener('resize', () => {
this.fitAddon.fit();
this.sendResize();
});
// Handle page unload
window.addEventListener('beforeunload', () => {
if (this.ws) {
this.ws.close();
}
});
}
connect() {
this.updateStatus('Connecting...', 'connecting');
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws`;
this.ws = new WebSocket(wsUrl);
this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
this.updateStatus('Connected', 'connected');
this.terminal.clear();
this.sendResize();
};
this.ws.onmessage = (event) => {
if (typeof event.data === 'string') {
this.terminal.write(event.data);
} else {
// Handle binary data
const reader = new FileReader();
reader.onload = () => {
const data = new Uint8Array(reader.result);
this.terminal.write(data);
};
reader.readAsArrayBuffer(event.data);
}
};
this.ws.onclose = (event) => {
console.log('WebSocket disconnected:', event.code, event.reason);
this.updateStatus('Disconnected', 'disconnected');
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = this.reconnectDelay * this.reconnectAttempts;
this.terminal.write(`\r\nConnection lost. Reconnecting in ${delay/1000}s...\r\n`);
setTimeout(() => {
this.connect();
}, delay);
} else {
this.terminal.write('\r\nFailed to reconnect. Please refresh the page.\r\n');
}
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
this.updateStatus('Connection Error', 'disconnected');
};
}
sendResize() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const dimensions = this.terminal;
const resizeMsg = {
cols: dimensions.cols,
rows: dimensions.rows
};
this.ws.send(JSON.stringify(resizeMsg));
}
}
updateStatus(text, className) {
const status = document.getElementById('status');
status.textContent = text;
status.className = `status ${className}`;
}
}
// Initialize terminal when page loads
document.addEventListener('DOMContentLoaded', () => {
new WebTerminal();
});
</script>
</body>
</html>