176 lines
6.1 KiB
HTML
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> |