718 lines
23 KiB
HTML
718 lines
23 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Retro Computing Action Controller</title>
|
|
<style>
|
|
/* Retro Terminal Theme */
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
padding: 20px;
|
|
background: #000000;
|
|
color: #00ff00;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 14px;
|
|
line-height: 1.4;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* CRT Monitor Effect */
|
|
body::before {
|
|
content: '';
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: linear-gradient(
|
|
rgba(18, 16, 16, 0) 50%,
|
|
rgba(0, 0, 0, 0.25) 50%
|
|
);
|
|
background-size: 100% 2px;
|
|
z-index: 1;
|
|
pointer-events: none;
|
|
animation: scanlines 8s linear infinite;
|
|
}
|
|
|
|
@keyframes scanlines {
|
|
0% { background-position: 0 0; }
|
|
100% { background-position: 0 10px; }
|
|
}
|
|
|
|
main {
|
|
position: relative;
|
|
z-index: 2;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
h1 {
|
|
color: #00ff00;
|
|
text-transform: uppercase;
|
|
letter-spacing: 2px;
|
|
font-size: 1.2em;
|
|
margin-bottom: 30px;
|
|
text-shadow: 0 0 10px #00ff00;
|
|
}
|
|
|
|
/* Terminal Container */
|
|
.terminal-controller {
|
|
background: #0a0a0a;
|
|
border: 2px solid #00ff00;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
box-shadow:
|
|
0 0 20px rgba(0, 255, 0, 0.5),
|
|
inset 0 0 20px rgba(0, 255, 0, 0.1);
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Terminal Header */
|
|
.terminal-header {
|
|
border-bottom: 1px solid #00ff00;
|
|
padding-bottom: 10px;
|
|
margin-bottom: 20px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.terminal-title {
|
|
color: #00ff00;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
.terminal-status {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.status-indicator {
|
|
width: 8px;
|
|
height: 8px;
|
|
background: #00ff00;
|
|
border-radius: 50%;
|
|
box-shadow: 0 0 5px #00ff00;
|
|
animation: blink 2s infinite;
|
|
}
|
|
|
|
@keyframes blink {
|
|
0%, 50% { opacity: 1; }
|
|
51%, 100% { opacity: 0.3; }
|
|
}
|
|
|
|
/* Command History */
|
|
.command-history {
|
|
height: 200px;
|
|
overflow-y: auto;
|
|
margin-bottom: 20px;
|
|
padding: 10px;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
border: 1px solid #00ff00;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.command-history::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
.command-history::-webkit-scrollbar-track {
|
|
background: #0a0a0a;
|
|
}
|
|
|
|
.command-history::-webkit-scrollbar-thumb {
|
|
background: #00ff00;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.history-entry {
|
|
margin-bottom: 8px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.history-entry.command {
|
|
color: #00ff00;
|
|
}
|
|
|
|
.history-entry.output {
|
|
color: #00cc00;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
.history-entry.error {
|
|
color: #ff3333;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
.history-entry.success {
|
|
color: #00ff00;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
/* Command Input Area */
|
|
.command-input-area {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
border: 1px solid #00ff00;
|
|
position: relative;
|
|
}
|
|
|
|
.prompt {
|
|
color: #00ff00;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.command-display {
|
|
flex: 1;
|
|
color: #00ff00;
|
|
position: relative;
|
|
}
|
|
|
|
.cursor {
|
|
display: inline-block;
|
|
width: 8px;
|
|
height: 14px;
|
|
background: #00ff00;
|
|
animation: cursor-blink 1s infinite;
|
|
vertical-align: text-bottom;
|
|
}
|
|
|
|
@keyframes cursor-blink {
|
|
0%, 49% { opacity: 1; }
|
|
50%, 100% { opacity: 0; }
|
|
}
|
|
|
|
/* Action Buttons */
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.terminal-button {
|
|
flex: 1;
|
|
background: transparent;
|
|
border: 1px solid #00ff00;
|
|
color: #00ff00;
|
|
padding: 12px 20px;
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 12px;
|
|
text-transform: uppercase;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.terminal-button:hover {
|
|
background: rgba(0, 255, 0, 0.1);
|
|
box-shadow: 0 0 10px rgba(0, 255, 0, 0.5);
|
|
text-shadow: 0 0 5px #00ff00;
|
|
}
|
|
|
|
.terminal-button:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.terminal-button.executing {
|
|
pointer-events: none;
|
|
animation: pulse 0.5s infinite;
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
|
|
/* Loading Animation */
|
|
.loading-animation {
|
|
display: none;
|
|
text-align: center;
|
|
padding: 20px;
|
|
color: #00ff00;
|
|
}
|
|
|
|
.loading-animation.active {
|
|
display: block;
|
|
}
|
|
|
|
.loading-bar {
|
|
width: 100%;
|
|
height: 20px;
|
|
border: 1px solid #00ff00;
|
|
margin: 10px 0;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.loading-progress {
|
|
height: 100%;
|
|
background: #00ff00;
|
|
width: 0%;
|
|
transition: width 0.3s;
|
|
box-shadow: 0 0 10px #00ff00;
|
|
}
|
|
|
|
.loading-text {
|
|
font-size: 12px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
/* Confirmation Dialog */
|
|
.confirmation-dialog {
|
|
display: none;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background: #000000;
|
|
border: 2px solid #00ff00;
|
|
padding: 20px;
|
|
z-index: 10;
|
|
box-shadow: 0 0 30px rgba(0, 255, 0, 0.8);
|
|
}
|
|
|
|
.confirmation-dialog.active {
|
|
display: block;
|
|
}
|
|
|
|
.confirmation-message {
|
|
margin-bottom: 20px;
|
|
color: #00ff00;
|
|
}
|
|
|
|
.confirmation-prompt {
|
|
color: #ffff00;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.confirmation-options {
|
|
display: flex;
|
|
gap: 20px;
|
|
justify-content: center;
|
|
}
|
|
|
|
.confirmation-option {
|
|
color: #00ff00;
|
|
cursor: pointer;
|
|
padding: 5px 10px;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.confirmation-option:hover {
|
|
border-color: #00ff00;
|
|
background: rgba(0, 255, 0, 0.1);
|
|
}
|
|
|
|
/* ASCII Art Elements */
|
|
.ascii-decoration {
|
|
color: #00ff00;
|
|
opacity: 0.3;
|
|
font-size: 10px;
|
|
line-height: 1;
|
|
white-space: pre;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
/* Sound indicator */
|
|
.sound-indicator {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
color: #00ff00;
|
|
font-size: 12px;
|
|
opacity: 0;
|
|
transition: opacity 0.3s;
|
|
}
|
|
|
|
.sound-indicator.active {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Glitch effect for errors */
|
|
@keyframes glitch {
|
|
0%, 100% {
|
|
text-shadow:
|
|
-2px 0 #ff0000,
|
|
2px 0 #00ffff;
|
|
}
|
|
25% {
|
|
text-shadow:
|
|
2px 0 #ff0000,
|
|
-2px 0 #00ffff;
|
|
}
|
|
50% {
|
|
text-shadow:
|
|
-2px 0 #ff0000,
|
|
2px 0 #00ffff;
|
|
}
|
|
}
|
|
|
|
.glitch {
|
|
animation: glitch 0.5s infinite;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<h1>Action Controller - Retro Computing Theme</h1>
|
|
|
|
<div class="terminal-controller">
|
|
<div class="terminal-header">
|
|
<div class="terminal-title">SYSTEM TERMINAL v3.1</div>
|
|
<div class="terminal-status">
|
|
<div class="status-indicator"></div>
|
|
<div class="status-indicator"></div>
|
|
<div class="status-indicator"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="ascii-decoration">
|
|
╔═══════════════════════════════════════════════════════════════╗
|
|
║ ┌─┐┌─┐┌┬┐┬┌─┐┌┐┌ ┌─┐┌─┐┌┐┌┌┬┐┬─┐┌─┐┬ ┬ ┌─┐┬─┐ ║
|
|
║ ├─┤│ │ ││ ││││ │ │ ││││ │ ├┬┘│ ││ │ ├┤ ├┬┘ ║
|
|
║ ┴ ┴└─┘ ┴ ┴└─┘┘└┘ └─┘└─┘┘└┘ ┴ ┴└─└─┘┴─┘┴─┘└─┘┴└─ ║
|
|
╚═══════════════════════════════════════════════════════════════╝
|
|
</div>
|
|
|
|
<div class="command-history" id="commandHistory">
|
|
<div class="history-entry command">> SYSTEM INITIALIZED</div>
|
|
<div class="history-entry output">Ready for commands...</div>
|
|
<div class="history-entry output">Type 'HELP' for available commands</div>
|
|
</div>
|
|
|
|
<div class="command-input-area">
|
|
<span class="prompt">></span>
|
|
<div class="command-display" id="commandDisplay">
|
|
<span id="currentCommand"></span><span class="cursor"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="loading-animation" id="loadingAnimation">
|
|
<div>EXECUTING COMMAND...</div>
|
|
<div class="loading-bar">
|
|
<div class="loading-progress" id="loadingProgress"></div>
|
|
</div>
|
|
<div class="loading-text" id="loadingText">Processing...</div>
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<button class="terminal-button" data-command="DEPLOY">
|
|
[F1] DEPLOY
|
|
</button>
|
|
<button class="terminal-button" data-command="ANALYZE">
|
|
[F2] ANALYZE
|
|
</button>
|
|
<button class="terminal-button" data-command="BACKUP">
|
|
[F3] BACKUP
|
|
</button>
|
|
<button class="terminal-button" data-command="SHUTDOWN">
|
|
[F4] SHUTDOWN
|
|
</button>
|
|
</div>
|
|
|
|
<div class="confirmation-dialog" id="confirmationDialog">
|
|
<div class="confirmation-message" id="confirmMessage"></div>
|
|
<div class="confirmation-prompt">CONFIRM ACTION? [Y/N]</div>
|
|
<div class="confirmation-options">
|
|
<span class="confirmation-option" id="confirmYes">[Y] YES</span>
|
|
<span class="confirmation-option" id="confirmNo">[N] NO</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sound-indicator" id="soundIndicator">♪ BEEP</div>
|
|
</main>
|
|
|
|
<script>
|
|
// Terminal State Management
|
|
const terminal = {
|
|
history: [],
|
|
currentCommand: '',
|
|
isExecuting: false,
|
|
commandQueue: []
|
|
};
|
|
|
|
// DOM Elements
|
|
const commandHistory = document.getElementById('commandHistory');
|
|
const currentCommandEl = document.getElementById('currentCommand');
|
|
const loadingAnimation = document.getElementById('loadingAnimation');
|
|
const loadingProgress = document.getElementById('loadingProgress');
|
|
const loadingText = document.getElementById('loadingText');
|
|
const confirmationDialog = document.getElementById('confirmationDialog');
|
|
const confirmMessage = document.getElementById('confirmMessage');
|
|
const soundIndicator = document.getElementById('soundIndicator');
|
|
|
|
// Command Definitions
|
|
const commands = {
|
|
DEPLOY: {
|
|
description: 'Deploy system updates',
|
|
requiresConfirmation: true,
|
|
loadingTime: 3000,
|
|
steps: [
|
|
'Initializing deployment sequence...',
|
|
'Checking system integrity...',
|
|
'Uploading files...',
|
|
'Verifying checksums...',
|
|
'Deployment complete!'
|
|
]
|
|
},
|
|
ANALYZE: {
|
|
description: 'Run system analysis',
|
|
requiresConfirmation: false,
|
|
loadingTime: 2000,
|
|
steps: [
|
|
'Starting analysis...',
|
|
'Scanning processes...',
|
|
'Checking memory usage...',
|
|
'Analysis complete!'
|
|
]
|
|
},
|
|
BACKUP: {
|
|
description: 'Create system backup',
|
|
requiresConfirmation: true,
|
|
loadingTime: 4000,
|
|
steps: [
|
|
'Preparing backup...',
|
|
'Compressing data...',
|
|
'Writing to storage...',
|
|
'Verifying backup integrity...',
|
|
'Backup successful!'
|
|
]
|
|
},
|
|
SHUTDOWN: {
|
|
description: 'Shutdown system',
|
|
requiresConfirmation: true,
|
|
loadingTime: 2000,
|
|
steps: [
|
|
'Initiating shutdown sequence...',
|
|
'Saving state...',
|
|
'Closing connections...',
|
|
'System shutdown complete.'
|
|
]
|
|
}
|
|
};
|
|
|
|
// Sound Effects (using Web Audio API)
|
|
function playBeep(frequency = 800, duration = 100) {
|
|
try {
|
|
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
|
const oscillator = audioContext.createOscillator();
|
|
const gainNode = audioContext.createGain();
|
|
|
|
oscillator.connect(gainNode);
|
|
gainNode.connect(audioContext.destination);
|
|
|
|
oscillator.frequency.value = frequency;
|
|
oscillator.type = 'square';
|
|
gainNode.gain.value = 0.1;
|
|
|
|
oscillator.start();
|
|
oscillator.stop(audioContext.currentTime + duration / 1000);
|
|
|
|
// Show sound indicator
|
|
soundIndicator.classList.add('active');
|
|
setTimeout(() => soundIndicator.classList.remove('active'), 300);
|
|
} catch (e) {
|
|
console.log('Audio not supported');
|
|
}
|
|
}
|
|
|
|
// Add entry to command history
|
|
function addHistoryEntry(text, type = 'command') {
|
|
const entry = document.createElement('div');
|
|
entry.className = `history-entry ${type}`;
|
|
entry.textContent = type === 'command' ? `> ${text}` : text;
|
|
commandHistory.appendChild(entry);
|
|
commandHistory.scrollTop = commandHistory.scrollHeight;
|
|
|
|
// Add to history array
|
|
terminal.history.push({ text, type, timestamp: new Date() });
|
|
}
|
|
|
|
// Show loading animation
|
|
async function showLoading(command) {
|
|
loadingAnimation.classList.add('active');
|
|
const steps = commands[command].steps;
|
|
const stepDuration = commands[command].loadingTime / steps.length;
|
|
|
|
for (let i = 0; i < steps.length; i++) {
|
|
loadingText.textContent = steps[i];
|
|
loadingProgress.style.width = `${((i + 1) / steps.length) * 100}%`;
|
|
playBeep(600 + (i * 100), 50);
|
|
await new Promise(resolve => setTimeout(resolve, stepDuration));
|
|
}
|
|
|
|
loadingAnimation.classList.remove('active');
|
|
loadingProgress.style.width = '0%';
|
|
}
|
|
|
|
// Show confirmation dialog
|
|
function showConfirmation(command) {
|
|
return new Promise((resolve) => {
|
|
confirmMessage.textContent = `Execute ${command} command?`;
|
|
confirmationDialog.classList.add('active');
|
|
playBeep(1000, 100);
|
|
|
|
const handleYes = () => {
|
|
confirmationDialog.classList.remove('active');
|
|
playBeep(1200, 50);
|
|
cleanup();
|
|
resolve(true);
|
|
};
|
|
|
|
const handleNo = () => {
|
|
confirmationDialog.classList.remove('active');
|
|
playBeep(400, 100);
|
|
cleanup();
|
|
resolve(false);
|
|
};
|
|
|
|
const handleKeypress = (e) => {
|
|
if (e.key.toLowerCase() === 'y') handleYes();
|
|
if (e.key.toLowerCase() === 'n') handleNo();
|
|
};
|
|
|
|
const cleanup = () => {
|
|
document.getElementById('confirmYes').removeEventListener('click', handleYes);
|
|
document.getElementById('confirmNo').removeEventListener('click', handleNo);
|
|
document.removeEventListener('keypress', handleKeypress);
|
|
};
|
|
|
|
document.getElementById('confirmYes').addEventListener('click', handleYes);
|
|
document.getElementById('confirmNo').addEventListener('click', handleNo);
|
|
document.addEventListener('keypress', handleKeypress);
|
|
});
|
|
}
|
|
|
|
// Execute command
|
|
async function executeCommand(command) {
|
|
if (terminal.isExecuting) return;
|
|
|
|
terminal.isExecuting = true;
|
|
|
|
// Add command to history
|
|
addHistoryEntry(command, 'command');
|
|
currentCommandEl.textContent = '';
|
|
|
|
// Check if command exists
|
|
if (!commands[command]) {
|
|
addHistoryEntry(`ERROR: Unknown command '${command}'`, 'error');
|
|
document.body.classList.add('glitch');
|
|
playBeep(200, 200);
|
|
setTimeout(() => document.body.classList.remove('glitch'), 500);
|
|
terminal.isExecuting = false;
|
|
return;
|
|
}
|
|
|
|
// Show command description
|
|
addHistoryEntry(commands[command].description, 'output');
|
|
|
|
// Check for confirmation
|
|
if (commands[command].requiresConfirmation) {
|
|
const confirmed = await showConfirmation(command);
|
|
if (!confirmed) {
|
|
addHistoryEntry('Command cancelled', 'output');
|
|
terminal.isExecuting = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Execute with loading animation
|
|
try {
|
|
await showLoading(command);
|
|
addHistoryEntry(`${command} executed successfully`, 'success');
|
|
playBeep(1600, 100);
|
|
setTimeout(() => playBeep(1800, 100), 150);
|
|
} catch (error) {
|
|
addHistoryEntry(`ERROR: ${command} failed - ${error.message}`, 'error');
|
|
playBeep(300, 300);
|
|
}
|
|
|
|
terminal.isExecuting = false;
|
|
}
|
|
|
|
// Handle button clicks
|
|
document.querySelectorAll('.terminal-button').forEach(button => {
|
|
button.addEventListener('click', async function() {
|
|
if (terminal.isExecuting) return;
|
|
|
|
const command = this.dataset.command;
|
|
this.classList.add('executing');
|
|
await executeCommand(command);
|
|
this.classList.remove('executing');
|
|
});
|
|
});
|
|
|
|
// Keyboard shortcuts
|
|
document.addEventListener('keydown', async (e) => {
|
|
if (terminal.isExecuting) return;
|
|
|
|
// Function key shortcuts
|
|
if (e.key === 'F1') {
|
|
e.preventDefault();
|
|
await executeCommand('DEPLOY');
|
|
} else if (e.key === 'F2') {
|
|
e.preventDefault();
|
|
await executeCommand('ANALYZE');
|
|
} else if (e.key === 'F3') {
|
|
e.preventDefault();
|
|
await executeCommand('BACKUP');
|
|
} else if (e.key === 'F4') {
|
|
e.preventDefault();
|
|
await executeCommand('SHUTDOWN');
|
|
}
|
|
|
|
// Type command
|
|
if (e.key.length === 1 && !e.ctrlKey && !e.metaKey) {
|
|
terminal.currentCommand += e.key.toUpperCase();
|
|
currentCommandEl.textContent = terminal.currentCommand;
|
|
playBeep(1000, 30);
|
|
}
|
|
|
|
// Enter to execute typed command
|
|
if (e.key === 'Enter' && terminal.currentCommand) {
|
|
await executeCommand(terminal.currentCommand);
|
|
terminal.currentCommand = '';
|
|
currentCommandEl.textContent = '';
|
|
}
|
|
|
|
// Backspace
|
|
if (e.key === 'Backspace' && terminal.currentCommand) {
|
|
terminal.currentCommand = terminal.currentCommand.slice(0, -1);
|
|
currentCommandEl.textContent = terminal.currentCommand;
|
|
playBeep(800, 30);
|
|
}
|
|
});
|
|
|
|
// Initial beep on load
|
|
window.addEventListener('load', () => {
|
|
setTimeout(() => {
|
|
playBeep(800, 100);
|
|
setTimeout(() => playBeep(1000, 100), 150);
|
|
setTimeout(() => playBeep(1200, 100), 300);
|
|
}, 500);
|
|
});
|
|
|
|
// Add some random flicker effect
|
|
setInterval(() => {
|
|
if (Math.random() > 0.95) {
|
|
document.body.style.opacity = '0.95';
|
|
setTimeout(() => {
|
|
document.body.style.opacity = '1';
|
|
}, 50);
|
|
}
|
|
}, 3000);
|
|
</script>
|
|
</body>
|
|
</html> |