CatPortal / modules /Terminal.js
NikaMimi's picture
Upload 17 files
06163ac verified
/**
* 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 = `<span class="${className}">${text}</span>`;
content.appendChild(line);
}
addTerminalOutput(content, text) {
const line = document.createElement('div');
line.className = 'terminal-line';
line.innerHTML = `<span class="terminal-output">${text}</span>`;
content.appendChild(line);
}
clearTerminal(content) {
content.innerHTML = '';
this.addTerminalLine(content, 'Terminal cleared! Ready for more cat commands! 🐱', 'terminal-success');
}
terminalHelp(content) {
const helpText = `
<span class="terminal-category">📁 Navigation:</span>
ls [path] - List directory contents
cd [directory] - Change directory
pwd - Show current directory
cat [file] - Display file contents
<span class="terminal-category">🔍 System:</span>
whoami - Display visitor information
ps aux - Show running processes
uptime - System uptime
date - Current date and time
history - Command history
clear - Clear terminal
<span class="terminal-category">🌐 Network:</span>
curl [url] - Fetch web content
git [command] - Git operations
npm [command] - NPM operations
<span class="terminal-category">🐱 Cat Commands:</span>
meow [message] - Cat responses
purr - Show happiness level
scratch - Stress relief
nap - Take a quick break
fortune - Cat wisdom
<span class="terminal-category">💡 Tips:</span>
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 = `
<span class="terminal-category">🕵️ Visitor Detective Results:</span>
┌─────────────────────────────────────┐
│ Browser: ${visitorInfo.browser}
│ Platform: ${visitorInfo.platform}
│ Screen: ${visitorInfo.screenWidth}x${visitorInfo.screenHeight}
│ Language: ${visitorInfo.language}
│ Visit Time: ${new Date(visitorInfo.visitTime).toLocaleString()}
└─────────────────────────────────────┘
<span class="terminal-success">*purrs* Nice to meet you, fellow human! 🐱</span>
`;
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 = `<span class="terminal-category">📂 Contents of ${targetPath}:</span>\n`;
contents.forEach(item => {
const type = item.type === 'dir' ? '<span class="terminal-directory">DIR</span>' : '<span class="terminal-file">FILE</span>';
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, `<span class="terminal-success">Changed to home directory 🏠</span>`);
return;
}
// Simple directory navigation simulation
if (path === '..') {
if (this.currentDirectory !== '~') {
const parts = this.currentDirectory.split('/');
parts.pop();
this.currentDirectory = parts.join('/') || '~';
this.addTerminalOutput(content, `<span class="terminal-success">Moved up one directory 📁</span>`);
} 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, `<span class="terminal-success">Changed to ${this.currentDirectory} 📁</span>`);
} 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, `<span class="terminal-success">${this.currentDirectory}</span>`);
}
terminalCat(content, filename) {
if (!filename) {
this.addTerminalOutput(content, 'cat: missing file operand 🙀\nUsage: cat <filename>');
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 `
<span class="terminal-category">${project.icon} ${project.title}</span>
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
<span class="terminal-success">Description:</span>
${project.description}
<span class="terminal-success">Technologies:</span>
${project.technologies.map(tech => `• ${tech}`).join('\n')}
<span class="terminal-success">Features:</span>
${project.features.map(feature => `🐾 ${feature}`).join('\n')}
<span class="terminal-success">Links:</span>
• GitHub: ${project.github}
• Demo: ${project.demo}
<span class="terminal-category">*purrs approvingly* 😸</span>
`;
}
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, `<span class="terminal-success">${dateString}</span>`);
}
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, `
<span class="terminal-category">😸 Current Happiness Level: ${happiness}%</span>
Status: ${purrLevel}
Mood: ${happiness > 70 ? "😻 Ecstatic" : happiness > 50 ? "😊 Happy" : "😐 Neutral"}
*${purrLevel.toLowerCase()}* 🐾
`);
}
terminalScratch(content) {
this.addTerminalOutput(content, `
<span class="terminal-category">🐾 *scratch scratch*</span>
Ahh, that's better! Stress levels reduced.
Your virtual scratching post has been used.
<span class="terminal-success">+10 Comfort Points! 😌</span>
`);
}
terminalNap(content) {
this.addTerminalOutput(content, `
<span class="terminal-category">😴 Taking a quick cat nap...</span>
*curls up in a sunny spot*
Zzz... 💤
<span class="terminal-success">Refreshed and ready! Energy restored! 🐱</span>
`);
}
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, `<span class="terminal-category">🔮 Cat Fortune:</span>\n${fortune}`);
}
terminalPs(content, args) {
const processes = `
<span class="terminal-category">🔄 CatOS Process Status:</span>
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
<span class="terminal-success">Current mood: Caffeinated and ready to code! ☕</span>
`;
this.addTerminalOutput(content, processes);
}
terminalUptime(content) {
const uptime = `
<span class="terminal-category">⏱️ System Uptime:</span>
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 😸
<span class="terminal-success">Load average: 1.33, 7.77, 42.00 (that's normal for a cat) 📊</span>
`;
this.addTerminalOutput(content, uptime);
}
terminalCurl(content, url) {
if (!url) {
this.addTerminalOutput(content, 'curl: no URL specified 🙀\nUsage: curl <url>');
return;
}
// Simulate curl with cat-themed responses
this.addTerminalOutput(content, `<span class="terminal-category">🌐 Fetching ${url}...</span>`);
setTimeout(() => {
if (url.includes('github.com')) {
this.addTerminalOutput(content, `
<span class="terminal-success">✅ Connected successfully!</span>
Repository found: Lots of cat-themed code! 😸
Stars: ⭐⭐⭐⭐⭐ (purr-fect rating)
Issues: 3 (all related to insufficient treats)
`);
} else {
this.addTerminalOutput(content, `
<span class="terminal-success">✅ Response received!</span>
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, `
<span class="terminal-category">📊 Git Status:</span>
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
<span class="terminal-success">*purrs* Everything looks good! 🐾</span>
`);
break;
case 'log':
this.addTerminalOutput(content, `
<span class="terminal-category">📝 Git Log:</span>
commit a1b2c3d (HEAD -> main, origin/main)
Author: Cat Developer <cat@catos.dev>
Date: Today
Fix: Improved treat dispensing algorithm 🍪
commit e4f5g6h
Author: Cat Developer <cat@catos.dev>
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, `
<span class="terminal-category">📦 Installing cat-packages...</span>
+ catnip@4.2.0
+ yarn-ball@1.3.7
+ treat-dispenser@2.1.0
+ purr-engine@8.0.1
<span class="terminal-success">✅ All packages installed successfully! 😸</span>
Note: Remember to pet your dependencies regularly.
`);
break;
case 'start':
this.addTerminalOutput(content, `
<span class="terminal-category">🚀 Starting development server...</span>
> 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 = '<span class="terminal-category">📚 Command History:</span>\n';
this.commandHistory.forEach((cmd, index) => {
output += `${index + 1}. ${cmd}\n`;
});
this.addTerminalOutput(content, output);
}
terminalExit(content) {
this.addTerminalOutput(content, `
<span class="terminal-category">👋 Goodbye!</span>
Thanks for using CatOS Terminal!
*purrs farewell* 😸
<span class="terminal-success">Tip: Close the window to fully exit, or keep coding! 🐾</span>
`);
}
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();