/* ========================================================================== SYSTEM AWAKENING - Terminal Module Simulated command-line interface with automatic log generation ========================================================================== */ import { ANIMATION, TERMINAL_LOGS, COLORS, formatTimestamp, getRandomValue } from './config.js'; /** * Terminal Class * Manages the simulated terminal interface and log generation */ export class Terminal { /** * Create a new terminal instance * @param {HTMLElement} container - Terminal container element */ constructor(container) { this.container = container; this.outputElement = container.querySelector('.terminal-output'); this.inputElement = container.querySelector('.terminal-input'); this.cursorElement = container.querySelector('.terminal-input .cursor'); // Log management this.logs = [...TERMINAL_LOGS]; this.currentLogIndex = 0; this.displayedLogs = []; // Animation state this.logInterval = null; this.isAutoScrolling = true; this.scrollAnimationId = null; this.lastLogTime = 0; // Interactive features this.isInteractive = false; this.commandHistory = []; this.historyIndex = -1; this.currentCommand = ''; // Initialize this.init(); } /** * Initialize the terminal */ init() { // Set up initial logs this.addInitialLogs(); // Start automatic log generation this.startAutoLogs(); // Bind interactive events this.bindEvents(); // Start cursor animation this.startCursorAnimation(); console.log('Terminal initialized'); } /** * Add initial log entries */ addInitialLogs() { // Clear existing logs (keeping the placeholder ones from HTML) const placeholderLogs = this.outputElement.querySelectorAll('.log-line'); placeholderLogs.forEach(log => log.remove()); // Add 5 initial logs for (let i = 0; i < 5; i++) { this.addLog(); } } /** * Start automatic log generation */ startAutoLogs() { // Clear any existing interval if (this.logInterval) { clearInterval(this.logInterval); } // Start new interval with random variation this.logInterval = setInterval(() => { this.addLog(); // Occasionally add multiple logs at once if (Math.random() > 0.7) { setTimeout(() => this.addLog(), getRandomValue(300, 800)); } // Occasionally simulate system events if (Math.random() > 0.9) { this.simulateSystemEvent(); } }, ANIMATION.LOG_INTERVAL); } /** * Add a new log entry * @param {string} customMessage - Optional custom message */ addLog(customMessage = null) { // Get message (either custom or from rotation) const message = customMessage || this.getNextLogMessage(); // Create log element const logElement = document.createElement('div'); logElement.className = 'log-line'; // Format timestamp and message const timestamp = formatTimestamp(); logElement.innerHTML = `${timestamp} ${message}`; // Add to output this.outputElement.appendChild(logElement); // Limit total displayed logs (performance) this.limitDisplayedLogs(); // Auto-scroll to bottom if (this.isAutoScrolling) { this.scrollToBottom(); } // Store reference this.displayedLogs.push({ element: logElement, timestamp: timestamp, message: message, time: Date.now() }); this.lastLogTime = Date.now(); } /** * Get the next log message from the rotation * @returns {string} Next log message */ getNextLogMessage() { // Get next message in rotation const message = this.logs[this.currentLogIndex]; // Move to next message, loop back to start this.currentLogIndex = (this.currentLogIndex + 1) % this.logs.length; return message; } /** * Limit the number of displayed logs for performance */ limitDisplayedLogs() { const maxLogs = 50; while (this.outputElement.children.length > maxLogs) { this.outputElement.removeChild(this.outputElement.firstChild); this.displayedLogs.shift(); } } /** * Simulate a system event or alert */ simulateSystemEvent() { const events = [ "SYSTEM ALERT: Quantum decoherence detected", "WARNING: Neural pathway saturation at 87%", "INFO: Temporal synchronization drift: +0.5ms", "NOTICE: Data sanctity verification complete", "ERROR: Process quantum_entanglement terminated unexpectedly", "SUCCESS: Consciousness fragment integration complete" ]; const event = events[Math.floor(Math.random() * events.length)]; this.addLog(event); // Add visual feedback for alerts if (event.includes('ALERT') || event.includes('WARNING') || event.includes('ERROR')) { this.flashTerminal(); } } /** * Flash the terminal for alerts */ flashTerminal() { // Add flash effect this.container.style.transition = 'box-shadow 0.3s ease'; this.container.style.boxShadow = `0 0 40px ${COLORS.ACCENT}`; // Reset after delay setTimeout(() => { this.container.style.boxShadow = ''; }, 500); } /** * Smooth scroll to bottom of terminal */ scrollToBottom() { if (this.scrollAnimationId) { cancelAnimationFrame(this.scrollAnimationId); } const targetScroll = this.outputElement.scrollHeight - this.outputElement.clientHeight; const currentScroll = this.outputElement.scrollTop; const distance = targetScroll - currentScroll; if (Math.abs(distance) < 1) { this.outputElement.scrollTop = targetScroll; return; } // Smooth scrolling with easing const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3); const duration = 300; // milliseconds let startTime = null; const animateScroll = (timestamp) => { if (!startTime) startTime = timestamp; const elapsed = timestamp - startTime; const progress = Math.min(elapsed / duration, 1); const easedProgress = easeOutCubic(progress); const newScroll = currentScroll + distance * easedProgress; this.outputElement.scrollTop = newScroll; if (progress < 1) { this.scrollAnimationId = requestAnimationFrame(animateScroll); } }; this.scrollAnimationId = requestAnimationFrame(animateScroll); } /** * Start cursor blinking animation */ startCursorAnimation() { if (!this.cursorElement) return; let isVisible = true; const blink = () => { isVisible = !isVisible; this.cursorElement.style.opacity = isVisible ? '1' : '0'; }; // Blink every 700ms setInterval(blink, 700); } /** * Bind event listeners for interactivity */ bindEvents() { // Tab switching const tabs = this.container.querySelectorAll('.tab'); tabs.forEach(tab => { tab.addEventListener('click', () => { this.switchTab(tab); }); }); // Terminal controls (minimize/maximize/close) const controls = this.container.querySelectorAll('.control-btn'); controls.forEach(btn => { btn.addEventListener('click', (e) => { e.stopPropagation(); this.handleControlClick(btn); }); }); // Auto-scroll toggle on user scroll this.outputElement.addEventListener('scroll', () => { const isAtBottom = this.outputElement.scrollHeight - this.outputElement.scrollTop - this.outputElement.clientHeight < 10; this.isAutoScrolling = isAtBottom; }); // Keyboard shortcuts for advanced users document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.key === 'l') { e.preventDefault(); this.clear(); } }); } /** * Switch terminal tabs * @param {HTMLElement} tab - Clicked tab element */ switchTab(tab) { // Remove active class from all tabs const tabs = this.container.querySelectorAll('.tab'); tabs.forEach(t => t.classList.remove('active')); // Add active class to clicked tab tab.classList.add('active'); // Simulate tab-specific content this.simulateTabContent(tab); } /** * Simulate content for different tabs * @param {HTMLElement} tab - Active tab element */ simulateTabContent(tab) { const tabText = tab.textContent || tab.innerText; // Clear existing logs this.clear(); // Add tab-specific initial logs if (tabText.includes('system.log')) { this.addLog("Loading system logs... [OK]"); this.addLog("Current system time: " + new Date().toISOString()); this.addLog("System uptime: 47 days, 3 hours, 22 minutes"); } else if (tabText.includes('processes.ctl')) { this.addLog("Process control interface active"); this.addLog("Active processes: 142"); this.addLog("CPU utilization: 34.7%"); this.addLog("Memory usage: 12.4 EXABYTES"); } else if (tabText.includes('network.dbg')) { this.addLog("Network diagnostics mode"); this.addLog("Connected nodes: 1024"); this.addLog("Latency: 0.3ms average"); this.addLog("Bandwidth: 1.2 petabits/sec"); } } /** * Handle control button clicks * @param {HTMLElement} btn - Clicked control button */ handleControlClick(btn) { const action = btn.classList.contains('minimize') ? 'minimize' : btn.classList.contains('maximize') ? 'maximize' : 'close'; switch (action) { case 'minimize': this.minimize(); break; case 'maximize': this.maximize(); break; case 'close': this.close(); break; } } /** * Minimize the terminal (simulated) */ minimize() { this.container.style.transform = 'scale(0.8)'; this.container.style.opacity = '0.7'; this.container.style.transition = 'all 0.3s ease'; // Restore after delay (simulated) setTimeout(() => { this.container.style.transform = ''; this.container.style.opacity = ''; }, 2000); } /** * Maximize the terminal (simulated) */ maximize() { const isMaximized = this.container.classList.contains('maximized'); if (!isMaximized) { this.container.classList.add('maximized'); this.container.style.width = '95%'; this.container.style.height = '80vh'; this.outputElement.style.height = 'calc(80vh - 120px)'; } else { this.container.classList.remove('maximized'); this.container.style.width = ''; this.container.style.height = ''; this.outputElement.style.height = ''; } } /** * Close the terminal (simulated) */ close() { this.container.style.opacity = '0.5'; this.container.style.transform = 'scale(0.95)'; this.container.style.transition = 'all 0.3s ease'; // Simulate reopening after delay setTimeout(() => { this.container.style.opacity = ''; this.container.style.transform = ''; // Add a "system restart" log this.addLog("Terminal session restored"); }, 1500); } /** * Clear all logs from the terminal */ clear() { // Clear displayed logs this.outputElement.innerHTML = ''; this.displayedLogs = []; // Add a fresh timestamp this.addLog("Terminal cleared - " + formatTimestamp()); } /** * Add a custom log message * @param {string} message - Custom log message */ log(message) { this.addLog(message); } /** * Enable or disable interactive features * @param {boolean} enabled - Whether interactive features are enabled */ setInteractive(enabled) { this.isInteractive = enabled; if (enabled) { this.enableInteractiveMode(); } else { this.disableInteractiveMode(); } } /** * Enable interactive command input mode */ enableInteractiveMode() { // This would set up command input functionality console.log('Interactive mode enabled (advanced feature)'); } /** * Disable interactive command input mode */ disableInteractiveMode() { // This would disable command input functionality console.log('Interactive mode disabled'); } /** * Get the current log count * @returns {number} Number of displayed logs */ getLogCount() { return this.displayedLogs.length; } /** * Get the last log timestamp * @returns {number} Timestamp of last log in milliseconds */ getLastLogTime() { return this.lastLogTime; } /** * Clean up resources */ dispose() { // Clear intervals if (this.logInterval) { clearInterval(this.logInterval); this.logInterval = null; } // Cancel animations if (this.scrollAnimationId) { cancelAnimationFrame(this.scrollAnimationId); } // Clear logs this.clear(); console.log('Terminal disposed'); } }