/** * CatOS Terminal Module * Handles terminal functionality and cat-themed commands */ class Terminal { constructor() { this.terminals = new Map(); this.commandHistory = []; this.historyIndex = -1; this.currentDirectory = '~'; // File system structure for simulation this.fileSystem = { '~': { type: 'dir', contents: { 'projects': { type: 'dir', contents: { 'sneaky-cat-proxy.md': { type: 'file', content: 'Project info...' }, 'cat-photo-gallery.md': { type: 'file', content: 'Gallery info...' }, 'robo-cat-manager.md': { type: 'file', content: 'Bot manager info...' } }}, 'documents': { type: 'dir', contents: { 'resume.pdf': { type: 'file', content: 'Professional resume' }, 'cat-wisdom.txt': { type: 'file', content: 'Ancient cat knowledge' } }}, 'pictures': { type: 'dir', contents: { 'profile.jpg': { type: 'file', content: 'Profile picture' }, 'cats': { type: 'dir', contents: { 'fluffy.jpg': { type: 'file', content: 'A fluffy cat' }, 'whiskers.png': { type: 'file', content: 'Cat with whiskers' } }} }}, 'README.md': { type: 'file', content: 'Welcome to CatOS!' } } } }; } initialize(terminalId, core) { this.core = core; const terminal = { id: terminalId, element: document.querySelector(`[data-terminal-id="${terminalId}"]`), input: document.getElementById(`terminal-input-${terminalId}`), content: document.getElementById(`terminal-content-${terminalId}`), prompt: document.getElementById(`prompt-${terminalId}`) }; if (!terminal.input || !terminal.content) return; this.terminals.set(terminalId, terminal); this.setupTerminalEvents(terminalId); terminal.input.focus(); } setupTerminalEvents(terminalId) { const terminal = this.terminals.get(terminalId); if (!terminal) return; terminal.input.addEventListener('keydown', (e) => { switch(e.key) { case 'Enter': this.handleCommand(terminalId); break; case 'ArrowUp': e.preventDefault(); this.navigateHistory(terminalId, -1); break; case 'ArrowDown': e.preventDefault(); this.navigateHistory(terminalId, 1); break; case 'Tab': e.preventDefault(); this.handleTabCompletion(terminalId); break; } }); // Focus input when clicking on terminal content terminal.content.addEventListener('click', () => { terminal.input.focus(); }); } handleCommand(terminalId) { const terminal = this.terminals.get(terminalId); if (!terminal) return; const command = terminal.input.value.trim(); if (!command) return; // Add command to history this.commandHistory.push(command); this.historyIndex = this.commandHistory.length; // Display command this.addTerminalLine(terminal.content, `${this.getPromptText()} ${command}`, 'terminal-prompt'); // Parse and execute command const [cmd, ...args] = command.split(' '); this.executeCommand(terminal.content, cmd.toLowerCase(), args); // Clear input and scroll to bottom terminal.input.value = ''; terminal.content.scrollTop = terminal.content.scrollHeight; } executeCommand(content, cmd, args) { switch(cmd) { case 'help': this.terminalHelp(content); break; case 'clear': this.clearTerminal(content); break; case 'ls': this.terminalLs(content, args[0]); break; case 'cd': this.terminalCd(content, args[0]); break; case 'pwd': this.terminalPwd(content); break; case 'cat': this.terminalCat(content, args[0]); break; case 'whoami': this.terminalWhoami(content, this.getVisitorInfo()); break; case 'date': this.terminalDate(content); break; case 'echo': this.terminalEcho(content, args.join(' ')); break; case 'meow': this.terminalMeow(content, args.join(' ')); break; case 'purr': this.terminalPurr(content); break; case 'scratch': this.terminalScratch(content); break; case 'nap': this.terminalNap(content); break; case 'fortune': this.terminalFortune(content); break; case 'ps': this.terminalPs(content, args); break; case 'uptime': this.terminalUptime(content); break; case 'curl': this.terminalCurl(content, args[0]); break; case 'git': this.terminalGit(content, args); break; case 'npm': this.terminalNpm(content, args); break; case 'history': this.terminalHistory(content); break; case 'exit': case 'quit': this.terminalExit(content); break; default: this.terminalCommandNotFound(content, cmd); } } getPromptText() { return `cat@catos:${this.currentDirectory}$`; } addTerminalLine(content, text, className = 'terminal-text') { const line = document.createElement('div'); line.className = 'terminal-line'; line.innerHTML = `${text}`; content.appendChild(line); } addTerminalOutput(content, text) { const line = document.createElement('div'); line.className = 'terminal-line'; line.innerHTML = `${text}`; content.appendChild(line); } clearTerminal(content) { content.innerHTML = ''; this.addTerminalLine(content, 'Terminal cleared! Ready for more cat commands! 🐱', 'terminal-success'); } terminalHelp(content) { const helpText = ` 📁 Navigation: ls [path] - List directory contents cd [directory] - Change directory pwd - Show current directory cat [file] - Display file contents 🔍 System: whoami - Display visitor information ps aux - Show running processes uptime - System uptime date - Current date and time history - Command history clear - Clear terminal 🌐 Network: curl [url] - Fetch web content git [command] - Git operations npm [command] - NPM operations 🐱 Cat Commands: meow [message] - Cat responses purr - Show happiness level scratch - Stress relief nap - Take a quick break fortune - Cat wisdom 💡 Tips: Use ↑/↓ arrows for command history Try: cat projects/sneaky-cat-proxy.md Pro tip: Type 'm' anywhere for surprise meows! 😸 `; this.addTerminalOutput(content, helpText); } terminalWhoami(content, visitorInfo) { const info = ` 🕵️ Visitor Detective Results: ┌─────────────────────────────────────┐ │ Browser: ${visitorInfo.browser} │ Platform: ${visitorInfo.platform} │ Screen: ${visitorInfo.screenWidth}x${visitorInfo.screenHeight} │ Language: ${visitorInfo.language} │ Visit Time: ${new Date(visitorInfo.visitTime).toLocaleString()} └─────────────────────────────────────┘ *purrs* Nice to meet you, fellow human! 🐱 `; this.addTerminalOutput(content, info); } getVisitorInfo() { return { browser: navigator.userAgent.split(' ').pop().split('/')[0] || 'Unknown', platform: navigator.platform || 'Unknown', screenWidth: screen.width, screenHeight: screen.height, language: navigator.language || 'Unknown', visitTime: Date.now() }; } terminalLs(content, path) { const targetPath = path || this.currentDirectory; const contents = this.getDirectoryContents(targetPath); if (!contents) { this.addTerminalOutput(content, `ls: cannot access '${targetPath}': No such file or directory 😿`); return; } let output = `📂 Contents of ${targetPath}:\n`; contents.forEach(item => { const type = item.type === 'dir' ? 'DIR' : 'FILE'; output += `${item.icon} ${type} ${item.name}\n`; }); this.addTerminalOutput(content, output); } getDirectoryContents(path) { // Navigate to the specified path in our file system let current = this.fileSystem['~']; if (path !== '~' && path !== '.') { const parts = path.replace(/^~\//, '').split('/').filter(p => p); for (const part of parts) { if (current.contents && current.contents[part]) { current = current.contents[part]; } else { return null; } } } if (current.type !== 'dir') return null; // Convert to display format const contents = []; if (current.contents) { for (const [name, item] of Object.entries(current.contents)) { contents.push({ name, type: item.type, icon: item.type === 'dir' ? '📁' : '📄' }); } } return contents; } terminalCd(content, path) { if (!path || path === '~') { this.currentDirectory = '~'; this.addTerminalOutput(content, `Changed to home directory 🏠`); return; } // Simple directory navigation simulation if (path === '..') { if (this.currentDirectory !== '~') { const parts = this.currentDirectory.split('/'); parts.pop(); this.currentDirectory = parts.join('/') || '~'; this.addTerminalOutput(content, `Moved up one directory 📁`); } else { this.addTerminalOutput(content, `Already at root directory! 🏠`); } return; } // Check if directory exists const contents = this.getDirectoryContents(this.currentDirectory); const targetDir = contents?.find(item => item.name === path && item.type === 'dir'); if (targetDir) { this.currentDirectory = this.currentDirectory === '~' ? `~/${path}` : `${this.currentDirectory}/${path}`; this.addTerminalOutput(content, `Changed to ${this.currentDirectory} 📁`); } else { this.addTerminalOutput(content, `cd: ${path}: No such directory 😿`); } // Update all terminal prompts this.terminals.forEach(terminal => { if (terminal.prompt) { terminal.prompt.textContent = this.getPromptText(); } }); } terminalPwd(content) { this.addTerminalOutput(content, `${this.currentDirectory}`); } terminalCat(content, filename) { if (!filename) { this.addTerminalOutput(content, 'cat: missing file operand 🙀\nUsage: cat '); return; } // Special handling for project files if (filename.includes('sneaky-cat-proxy')) { const project = this.core.appManager.projects['sneaky-cat-proxy']; this.addTerminalOutput(content, this.formatProjectInfo(project)); } else if (filename.includes('cat-photo-gallery')) { const project = this.core.appManager.projects['cat-photo-gallery']; this.addTerminalOutput(content, this.formatProjectInfo(project)); } else if (filename.includes('robo-cat-manager')) { const project = this.core.appManager.projects['robo-cat-manager']; this.addTerminalOutput(content, this.formatProjectInfo(project)); } else { this.addTerminalOutput(content, `cat: ${filename}: No such file or directory 😿`); } } formatProjectInfo(project) { return ` ${project.icon} ${project.title} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Description: ${project.description} Technologies: ${project.technologies.map(tech => `• ${tech}`).join('\n')} Features: ${project.features.map(feature => `🐾 ${feature}`).join('\n')} Links: • GitHub: ${project.github} • Demo: ${project.demo} *purrs approvingly* 😸 `; } terminalDate(content) { const now = new Date(); const dateString = now.toLocaleString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short' }); this.addTerminalOutput(content, `${dateString}`); } terminalEcho(content, message) { this.addTerminalOutput(content, message || ''); } // Cat-specific commands terminalMeow(content, message) { const catResponses = [ "🐱 *meows back* Purrfect!", "😸 *purrs* That's interesting!", "😺 *head bumps* I agree!", "🙀 *surprised meow* Really?!", "😻 *affectionate meow* Aww!", "😹 *laughing meow* Haha!", "😾 *grumpy meow* Hmph!" ]; if (message) { const response = catResponses[Math.floor(Math.random() * catResponses.length)]; this.addTerminalOutput(content, `You said: "${message}"\n${response}`); } else { this.addTerminalOutput(content, catResponses[Math.floor(Math.random() * catResponses.length)]); } } terminalPurr(content) { const happiness = Math.floor(Math.random() * 100) + 1; const purrLevel = happiness > 80 ? "MAXIMUM PURR" : happiness > 60 ? "Happy purrs" : happiness > 40 ? "Content purrs" : "Quiet purrs"; this.addTerminalOutput(content, ` 😸 Current Happiness Level: ${happiness}% Status: ${purrLevel} Mood: ${happiness > 70 ? "😻 Ecstatic" : happiness > 50 ? "😊 Happy" : "😐 Neutral"} *${purrLevel.toLowerCase()}* 🐾 `); } terminalScratch(content) { this.addTerminalOutput(content, ` 🐾 *scratch scratch* Ahh, that's better! Stress levels reduced. Your virtual scratching post has been used. +10 Comfort Points! 😌 `); } terminalNap(content) { this.addTerminalOutput(content, ` 😴 Taking a quick cat nap... *curls up in a sunny spot* Zzz... 💤 Refreshed and ready! Energy restored! 🐱 `); } terminalFortune(content) { const fortunes = [ '🐾 A warm laptop keyboard is worth two in the bush.', '🐱 The best code is written at 3 AM with a cat on your keyboard.', '😸 In the kingdom of bugs, the debugger is king.', '🎯 A feature is only as good as its documentation... said no cat ever.', '💻 The cloud is just someone else\'s computer, probably with better WiFi.', '🐾 Remember: There are no mistakes, only happy little bugs.', '🐱 The cloud is just other people\'s litter boxes.' ]; const fortune = fortunes[Math.floor(Math.random() * fortunes.length)]; this.addTerminalOutput(content, `🔮 Cat Fortune:\n${fortune}`); } terminalPs(content, args) { const processes = ` 🔄 CatOS Process Status: USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND cat 1337 15.2 8.5 245760 32156 pts/0 Sl+ 09:30 0:42 /usr/bin/coffee-maker cat 2020 12.1 4.2 128000 16384 pts/1 S 09:32 0:15 /usr/bin/yarn-ball-tracker cat 3000 8.7 2.1 64000 8192 pts/2 R 09:35 0:08 /usr/bin/mouse-detector cat 4040 5.3 1.8 32000 4096 pts/3 S 09:38 0:03 /usr/bin/nap-scheduler rafael 9999 98.5 75.2 2048000 234567 pts/5 R+ 09:35 5:67 /usr/bin/coding-furiously Current mood: Caffeinated and ready to code! ☕ `; this.addTerminalOutput(content, processes); } terminalUptime(content) { const uptime = ` ⏱️ System Uptime: Developer: 5+ years of coding experience Coffee Machine: 3 hours since last refill ☕ Cat OS: 42 days, 13 hours, 37 minutes (no crashes!) Motivation Level: 87% (pretty good for a Monday!) Purr Engine: Running at optimal frequency 😸 Load average: 1.33, 7.77, 42.00 (that's normal for a cat) 📊 `; this.addTerminalOutput(content, uptime); } terminalCurl(content, url) { if (!url) { this.addTerminalOutput(content, 'curl: no URL specified 🙀\nUsage: curl '); return; } // Simulate curl with cat-themed responses this.addTerminalOutput(content, `🌐 Fetching ${url}...`); setTimeout(() => { if (url.includes('github.com')) { this.addTerminalOutput(content, ` ✅ Connected successfully! Repository found: Lots of cat-themed code! 😸 Stars: ⭐⭐⭐⭐⭐ (purr-fect rating) Issues: 3 (all related to insufficient treats) `); } else { this.addTerminalOutput(content, ` ✅ Response received! Status: 200 OK 😺 Content-Type: text/purr-fect Cat-Approval: 100% `); } }, 1000); } terminalGit(content, args) { const subcommand = args[0] || 'status'; switch(subcommand) { case 'status': this.addTerminalOutput(content, ` 📊 Git Status: On branch main Your branch is up to date with 'origin/main'. Changes not staged for commit: modified: src/cat-behavior.js modified: config/treats.json Untracked files: hairball.log *purrs* Everything looks good! 🐾 `); break; case 'log': this.addTerminalOutput(content, ` 📝 Git Log: commit a1b2c3d (HEAD -> main, origin/main) Author: Cat Developer Date: Today Fix: Improved treat dispensing algorithm 🍪 commit e4f5g6h Author: Cat Developer Date: Yesterday Feature: Added nap scheduling system 😴 `); break; default: this.addTerminalOutput(content, `git ${subcommand}: Not implemented yet, but it sounds purr-fessional! 🐱‍💻`); } } terminalNpm(content, args) { const subcommand = args[0] || 'help'; switch(subcommand) { case 'install': this.addTerminalOutput(content, ` 📦 Installing cat-packages... + catnip@4.2.0 + yarn-ball@1.3.7 + treat-dispenser@2.1.0 + purr-engine@8.0.1 ✅ All packages installed successfully! 😸 Note: Remember to pet your dependencies regularly. `); break; case 'start': this.addTerminalOutput(content, ` 🚀 Starting development server... > CatOS@9.0.0 start > cat-dev-server --purr Local: http://localhost:3000 🐾 Network: http://192.168.1.100:3000 `); break; case 'run': this.addTerminalOutput(content, `Available scripts: start, build, test, purr, nap`); break; default: this.addTerminalOutput(content, `npm ${subcommand}: Command not found. Try 'npm run purr' instead! 😹`); } } terminalHistory(content) { if (this.commandHistory.length === 0) { this.addTerminalOutput(content, 'No commands in history yet! 📜'); return; } let output = '📚 Command History:\n'; this.commandHistory.forEach((cmd, index) => { output += `${index + 1}. ${cmd}\n`; }); this.addTerminalOutput(content, output); } terminalExit(content) { this.addTerminalOutput(content, ` 👋 Goodbye! Thanks for using CatOS Terminal! *purrs farewell* 😸 Tip: Close the window to fully exit, or keep coding! 🐾 `); } terminalCommandNotFound(content, cmd) { const suggestions = [ "Maybe you meant 'meow'? 🐱", "Try 'help' for available commands! 📖", "That's not a valid cat command! 😹", "*confused cat noises* 🙀", "Command not found in the litter box! 📦" ]; const suggestion = suggestions[Math.floor(Math.random() * suggestions.length)]; this.addTerminalOutput(content, `${cmd}: command not found\n${suggestion}`); } // History navigation navigateHistory(terminalId, direction) { const terminal = this.terminals.get(terminalId); if (!terminal || this.commandHistory.length === 0) return; this.historyIndex += direction; if (this.historyIndex < 0) { this.historyIndex = 0; } else if (this.historyIndex >= this.commandHistory.length) { this.historyIndex = this.commandHistory.length; terminal.input.value = ''; return; } terminal.input.value = this.commandHistory[this.historyIndex]; } // Tab completion (basic) handleTabCompletion(terminalId) { const terminal = this.terminals.get(terminalId); if (!terminal) return; const input = terminal.input.value; const commands = ['help', 'clear', 'ls', 'cd', 'pwd', 'cat', 'whoami', 'date', 'echo', 'meow', 'purr', 'scratch', 'nap', 'fortune', 'ps', 'uptime', 'curl', 'git', 'npm', 'history', 'exit']; const matches = commands.filter(cmd => cmd.startsWith(input)); if (matches.length === 1) { terminal.input.value = matches[0]; } else if (matches.length > 1) { this.addTerminalOutput(terminal.content, `Possible completions: ${matches.join(', ')}`); } } } // Export for use in main system window.CatOSTerminal = new Terminal();