mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
336 lines
9.9 KiB
HTML
336 lines
9.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Terminal Tamagotchi</title>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
background: #000;
|
|
font-family: 'Courier New', 'Monaco', 'Menlo', monospace;
|
|
color: #00ff00;
|
|
overflow: hidden;
|
|
height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.terminal-header {
|
|
background: #333;
|
|
padding: 8px;
|
|
border-bottom: 2px solid #666;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.terminal-button {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
}
|
|
|
|
.terminal-button.close { background: #ff5f56; }
|
|
.terminal-button.minimize { background: #ffbd2e; }
|
|
.terminal-button.maximize { background: #27c93f; }
|
|
|
|
.terminal-title {
|
|
color: #fff;
|
|
font-size: 14px;
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.terminal-body {
|
|
flex: 1;
|
|
padding: 25px;
|
|
overflow: hidden; /* No scrolling at all */
|
|
font-size: 18px; /* Much bigger font */
|
|
line-height: 1.5; /* More spacing */
|
|
white-space: pre-wrap;
|
|
background: #0a0a0a;
|
|
max-height: calc(100vh - 120px); /* More height */
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.terminal-body::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
.terminal-body::-webkit-scrollbar-track {
|
|
background: #1a1a1a;
|
|
}
|
|
|
|
.terminal-body::-webkit-scrollbar-thumb {
|
|
background: #333;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.terminal-body::-webkit-scrollbar-thumb:hover {
|
|
background: #555;
|
|
}
|
|
|
|
.terminal-input {
|
|
background: #0a0a0a;
|
|
border: none;
|
|
color: #00ff00;
|
|
font-family: 'Courier New', 'Monaco', 'Menlo', monospace;
|
|
font-size: 18px; /* Match terminal body */
|
|
padding: 12px 25px; /* Bigger padding */
|
|
outline: none;
|
|
width: 100%;
|
|
border-top: 1px solid #333;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.terminal-input::placeholder {
|
|
color: #666;
|
|
}
|
|
|
|
.typing-indicator {
|
|
color: #ffff00;
|
|
animation: blink 1s infinite;
|
|
}
|
|
|
|
@keyframes blink {
|
|
0%, 50% { opacity: 1; }
|
|
51%, 100% { opacity: 0; }
|
|
}
|
|
|
|
.cursor {
|
|
display: inline-block;
|
|
width: 8px;
|
|
height: 16px;
|
|
background: #00ff00;
|
|
animation: cursor-blink 1s infinite;
|
|
vertical-align: text-bottom;
|
|
}
|
|
|
|
@keyframes cursor-blink {
|
|
0%, 49% { opacity: 1; }
|
|
50%, 100% { opacity: 0; }
|
|
}
|
|
|
|
.status-bar {
|
|
background: #333;
|
|
color: #00ff00;
|
|
padding: 8px 25px; /* Bigger padding */
|
|
font-size: 14px; /* Bigger status bar */
|
|
border-top: 1px solid #666;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.status-bar > div {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.status-bar > div:first-child {
|
|
text-align: left;
|
|
}
|
|
|
|
.status-bar > div:last-child {
|
|
text-align: right;
|
|
}
|
|
|
|
.activity-detected {
|
|
color: #ff6b6b;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* Scanlines effect */
|
|
.terminal-body::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(
|
|
transparent 50%,
|
|
rgba(0, 255, 0, 0.03) 50%
|
|
);
|
|
background-size: 100% 4px;
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
}
|
|
|
|
.terminal-wrapper {
|
|
position: relative;
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.terminal-content {
|
|
position: relative;
|
|
z-index: 2;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="terminal-header">
|
|
<button class="terminal-button close"></button>
|
|
<button class="terminal-button minimize"></button>
|
|
<button class="terminal-button maximize"></button>
|
|
<div class="terminal-title">terminal-tamagotchi@myclub: ~$</div>
|
|
</div>
|
|
|
|
<div class="terminal-wrapper">
|
|
<div class="terminal-body" id="terminal">
|
|
<div class="terminal-content" id="terminalContent">
|
|
Initializing terminal monitor...
|
|
Scanning project files...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<input type="text" class="terminal-input" id="terminalInput" placeholder="Enter command (f=feed, p=play, r=refresh, q=quit)..." autocomplete="off">
|
|
|
|
<div class="status-bar">
|
|
<div id="statusLeft">Ready</div>
|
|
<div id="statusCenter">No activity</div>
|
|
<div id="statusRight">Connected</div>
|
|
</div>
|
|
|
|
<script>
|
|
const socket = io();
|
|
let commandHistory = [];
|
|
let historyIndex = -1;
|
|
|
|
const terminal = document.getElementById('terminalContent');
|
|
const input = document.getElementById('terminalInput');
|
|
const statusLeft = document.getElementById('statusLeft');
|
|
const statusCenter = document.getElementById('statusCenter');
|
|
const statusRight = document.getElementById('statusRight');
|
|
|
|
function updateTerminal(data) {
|
|
terminal.textContent = data.output;
|
|
|
|
// Update status - simplified to avoid conflicts
|
|
if (data.typing_detected) {
|
|
statusLeft.textContent = `ACTIVITY: ${data.recent_changes} files`;
|
|
statusLeft.className = 'activity-detected';
|
|
|
|
// Extract current file info from terminal output
|
|
const lines = data.output.split('\n');
|
|
const currentFileLine = lines.find(line => line.includes('File:'));
|
|
if (currentFileLine) {
|
|
const fileName = currentFileLine.split('File:')[1]?.trim();
|
|
statusCenter.textContent = `Editing: ${fileName}`;
|
|
} else {
|
|
statusCenter.textContent = 'Files being modified...';
|
|
}
|
|
} else {
|
|
statusLeft.textContent = 'Monitoring...';
|
|
statusLeft.className = '';
|
|
statusCenter.textContent = 'No activity';
|
|
}
|
|
|
|
// Auto-scroll to bottom
|
|
const terminalBody = document.getElementById('terminal');
|
|
terminalBody.scrollTop = terminalBody.scrollHeight;
|
|
}
|
|
|
|
function sendCommand(cmd) {
|
|
if (!cmd.trim()) return;
|
|
|
|
// Add to history
|
|
commandHistory.push(cmd);
|
|
historyIndex = commandHistory.length;
|
|
|
|
// Show command in terminal
|
|
terminal.textContent += `\n$ ${cmd}\n`;
|
|
|
|
// Send to server
|
|
console.log("Sending command:", cmd); // Debug log
|
|
socket.emit('command', { command: cmd });
|
|
|
|
// Clear input
|
|
input.value = '';
|
|
}
|
|
|
|
// Socket events
|
|
socket.on('connect', () => {
|
|
statusRight.textContent = 'Connected';
|
|
statusRight.style.color = '#00ff00';
|
|
});
|
|
|
|
socket.on('disconnect', () => {
|
|
statusRight.textContent = 'Disconnected';
|
|
statusRight.style.color = '#ff0000';
|
|
});
|
|
|
|
socket.on('terminal_update', (data) => {
|
|
updateTerminal(data);
|
|
});
|
|
|
|
socket.on('quit', (data) => {
|
|
terminal.textContent += `\n${data.message}\nConnection closed.\n`;
|
|
input.disabled = true;
|
|
input.placeholder = 'Session ended';
|
|
});
|
|
|
|
// Input handling
|
|
input.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') {
|
|
sendCommand(input.value);
|
|
} else if (e.key === 'ArrowUp') {
|
|
e.preventDefault();
|
|
if (historyIndex > 0) {
|
|
historyIndex--;
|
|
input.value = commandHistory[historyIndex];
|
|
}
|
|
} else if (e.key === 'ArrowDown') {
|
|
e.preventDefault();
|
|
if (historyIndex < commandHistory.length - 1) {
|
|
historyIndex++;
|
|
input.value = commandHistory[historyIndex];
|
|
} else {
|
|
historyIndex = commandHistory.length;
|
|
input.value = '';
|
|
}
|
|
}
|
|
});
|
|
|
|
// Focus input on page load
|
|
window.addEventListener('load', () => {
|
|
input.focus();
|
|
|
|
// Load initial data
|
|
fetch('/api/terminal-data')
|
|
.then(response => response.json())
|
|
.then(data => updateTerminal(data))
|
|
.catch(error => {
|
|
terminal.textContent += `Error loading data: ${error}\n`;
|
|
});
|
|
});
|
|
|
|
// Keep focus on terminal
|
|
document.addEventListener('click', () => {
|
|
input.focus();
|
|
});
|
|
|
|
// Add some terminal-like effects
|
|
setInterval(() => {
|
|
if (Math.random() < 0.01) { // 1% chance of flicker
|
|
terminal.style.opacity = '0.9';
|
|
setTimeout(() => {
|
|
terminal.style.opacity = '1';
|
|
}, 50);
|
|
}
|
|
}, 1000);
|
|
</script>
|
|
</body>
|
|
</html>
|