CatPortal / script.js
NikaMimi's picture
Upload 17 files
06163ac verified
/**
* CatOS v9.0 (Whiskers Edition) 🐱‍💻
* A pseudo desktop operating system portfolio
*/
// CatOS Core System
const CatOS = {
// System state
windows: new Map(),
apps: new Map(),
nextWindowId: 1,
focusedWindow: null,
startMenuOpen: false,
// Project data
projects: {
'sneaky-cat-proxy': {
title: 'Sneaky Cat Proxy',
icon: '🔗',
description: 'A purr-fessional proxy that helps cats navigate the internet stealthily! 🕵️‍♀️',
technologies: ['Node.js', 'Express', 'Stealth'],
features: [
'Stealth browsing capabilities',
'Cat-safe internet filtering',
'Automatic mouse toy detection',
'Built-in treat dispenser API'
],
github: 'https://github.com/catcoder/sneaky-proxy',
demo: 'https://sneaky-cat-proxy.vercel.app'
},
'cat-photo-gallery': {
title: 'Cat Photo Gallery',
icon: '📸',
description: 'A claw-some gallery to showcase all your favorite cat pics with purr-fect filtering! 😻',
technologies: ['React', 'TypeScript', 'Tailwind'],
features: [
'Advanced cat photo filtering',
'Automatic whisker detection',
'Paw print watermarking',
'Social sharing for cat influencers'
],
github: 'https://github.com/catcoder/cat-gallery',
demo: 'https://cat-gallery.vercel.app'
},
'robo-cat-manager': {
title: 'Robo-Cat Manager',
icon: '🤖',
description: 'Managing Discord bots like herding cats! Keep your digital kitties in line with style! 🐱‍💻',
technologies: ['Discord.js', 'MongoDB', 'Docker'],
features: [
'Multi-bot management dashboard',
'Automatic yarn ball deployment',
'Cat behavior analytics',
'Emergency tuna button'
],
github: 'https://github.com/catcoder/robo-cat-manager',
demo: 'https://discord-bot-manager.vercel.app'
}
},
// Initialize CatOS
init() {
this.showLoadingScreen();
this.setupEventListeners();
this.updateClock();
this.registerApps();
this.setRandomWallpaper();
// Boot sequence
setTimeout(() => {
this.hideLoadingScreen();
this.playStartupSound();
this.showWelcomeMessage();
}, 3000);
},
// Event Listeners
setupEventListeners() {
// Desktop icon interactions
document.querySelectorAll('.desktop-icon').forEach(icon => {
icon.addEventListener('dblclick', this.handleIconDoubleClick.bind(this));
icon.addEventListener('click', this.handleIconClick.bind(this));
});
// Start button
document.getElementById('start-button').addEventListener('click', this.toggleStartMenu.bind(this));
// Start menu apps
document.querySelectorAll('.menu-app').forEach(app => {
app.addEventListener('click', this.handleMenuAppClick.bind(this));
});
// Desktop context menu
document.getElementById('desktop').addEventListener('contextmenu', this.showContextMenu.bind(this));
// Close context menu on click
document.addEventListener('click', this.hideContextMenu.bind(this));
// Close start menu on outside click
document.addEventListener('click', (e) => {
if (!e.target.closest('.start-button') && !e.target.closest('.start-menu')) {
this.hideStartMenu();
}
});
// Keyboard shortcuts
document.addEventListener('keydown', this.handleKeyboard.bind(this));
// Window container clicks (deselect icons)
document.getElementById('windows-container').addEventListener('click', (e) => {
if (e.target.id === 'windows-container') {
this.deselectAllIcons();
}
});
},
// Icon interactions
handleIconClick(e) {
this.deselectAllIcons();
e.currentTarget.classList.add('selected');
},
handleIconDoubleClick(e) {
const icon = e.currentTarget;
const appType = icon.dataset.app;
const projectId = icon.dataset.project;
if (appType === 'project-viewer' && projectId) {
this.launchApp('project-viewer', { projectId });
} else {
this.launchApp(appType);
}
},
deselectAllIcons() {
document.querySelectorAll('.desktop-icon.selected').forEach(icon => {
icon.classList.remove('selected');
});
},
// Start Menu
toggleStartMenu() {
const menu = document.getElementById('start-menu');
const button = document.getElementById('start-button');
if (this.startMenuOpen) {
this.hideStartMenu();
} else {
menu.classList.add('show');
button.classList.add('active');
this.startMenuOpen = true;
}
},
hideStartMenu() {
const menu = document.getElementById('start-menu');
const button = document.getElementById('start-button');
menu.classList.remove('show');
button.classList.remove('active');
this.startMenuOpen = false;
},
handleMenuAppClick(e) {
const appType = e.currentTarget.dataset.app;
this.launchApp(appType);
this.hideStartMenu();
},
// Context Menu
showContextMenu(e) {
e.preventDefault();
const menu = document.getElementById('context-menu');
menu.style.left = e.clientX + 'px';
menu.style.top = e.clientY + 'px';
menu.classList.add('show');
},
hideContextMenu() {
document.getElementById('context-menu').classList.remove('show');
},
// App Management
registerApps() {
this.apps.set('terminal', {
title: 'Terminal',
icon: '💻',
create: () => this.createTerminalApp()
});
this.apps.set('project-viewer', {
title: 'Project Viewer',
icon: '📁',
create: (options) => this.createProjectViewerApp(options)
});
this.apps.set('about', {
title: 'About CatOS',
icon: '📋',
create: () => this.createAboutApp()
});
this.apps.set('contact', {
title: 'Contact',
icon: '📧',
create: () => this.createContactApp()
});
this.apps.set('file-explorer', {
title: 'File Explorer',
icon: '📁',
create: () => this.createFileExplorerApp()
});
},
launchApp(appType, options = {}) {
const app = this.apps.get(appType);
if (!app) {
this.showNotification(`App '${appType}' not found! 🙀`, 'error');
return;
}
// Check if app is already running (except project-viewer which can have multiple instances)
if (appType !== 'project-viewer') {
for (const [windowId, window] of this.windows) {
if (window.appType === appType) {
this.focusWindow(windowId);
return;
}
}
}
const content = app.create(options);
const windowId = this.createWindow({
title: app.title,
icon: app.icon,
content: content,
appType: appType
});
this.addToTaskbar(windowId, app);
},
// Window Management
createWindow(options) {
const windowId = `window-${this.nextWindowId++}`;
const window = document.createElement('div');
window.className = 'window focused';
window.id = windowId;
window.innerHTML = `
<div class="window-titlebar">
<div class="window-title">
<span class="window-icon">${options.icon}</span>
${options.title}
</div>
<div class="window-controls">
<button class="window-control minimize" data-action="minimize">−</button>
<button class="window-control maximize" data-action="maximize">□</button>
<button class="window-control close" data-action="close">×</button>
</div>
</div>
<div class="window-content">
${options.content}
</div>
<div class="window-resize-handle resize-handle-n" data-direction="n"></div>
<div class="window-resize-handle resize-handle-s" data-direction="s"></div>
<div class="window-resize-handle resize-handle-e" data-direction="e"></div>
<div class="window-resize-handle resize-handle-w" data-direction="w"></div>
<div class="window-resize-handle resize-handle-ne" data-direction="ne"></div>
<div class="window-resize-handle resize-handle-nw" data-direction="nw"></div>
<div class="window-resize-handle resize-handle-se" data-direction="se"></div>
<div class="window-resize-handle resize-handle-sw" data-direction="sw"></div>
`;
// Position window
const offset = (this.nextWindowId - 2) * 30;
window.style.left = (100 + offset) + 'px';
window.style.top = (50 + offset) + 'px';
window.style.width = options.width || '600px';
window.style.height = options.height || '400px';
// Add to container
document.getElementById('windows-container').appendChild(window);
// Store window data
this.windows.set(windowId, {
element: window,
title: options.title,
icon: options.icon,
appType: options.appType,
minimized: false,
maximized: false
});
// Setup window events
this.setupWindowEvents(windowId);
this.focusWindow(windowId);
return windowId;
},
setupWindowEvents(windowId) {
const window = this.windows.get(windowId);
const element = window.element;
// Window controls
element.querySelectorAll('.window-control').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
const action = btn.dataset.action;
switch(action) {
case 'minimize':
this.minimizeWindow(windowId);
break;
case 'maximize':
this.toggleMaximizeWindow(windowId);
break;
case 'close':
this.closeWindow(windowId);
break;
}
});
});
// Window focus
element.addEventListener('mousedown', () => {
this.focusWindow(windowId);
});
// Window dragging
const titlebar = element.querySelector('.window-titlebar');
this.makeDraggable(element, titlebar);
// Window resizing
this.setupWindowResizing(windowId);
},
focusWindow(windowId) {
// Unfocus all windows
document.querySelectorAll('.window').forEach(w => {
w.classList.remove('focused');
});
// Update taskbar
document.querySelectorAll('.taskbar-app').forEach(app => {
app.classList.remove('focused');
});
// Focus target window
const window = this.windows.get(windowId);
if (window) {
window.element.classList.add('focused');
this.focusedWindow = windowId;
// Update taskbar
const taskbarApp = document.querySelector(`[data-window-id="${windowId}"]`);
if (taskbarApp) {
taskbarApp.classList.add('focused');
}
}
},
minimizeWindow(windowId) {
const window = this.windows.get(windowId);
if (window) {
window.element.classList.add('minimized');
window.minimized = true;
// Update taskbar
const taskbarApp = document.querySelector(`[data-window-id="${windowId}"]`);
if (taskbarApp) {
taskbarApp.classList.remove('focused');
}
}
},
toggleMaximizeWindow(windowId) {
const window = this.windows.get(windowId);
if (window) {
if (window.maximized) {
window.element.classList.remove('maximized');
window.maximized = false;
} else {
window.element.classList.add('maximized');
window.maximized = true;
}
}
},
closeWindow(windowId) {
const window = this.windows.get(windowId);
if (window) {
// Remove from DOM
window.element.remove();
// Remove from taskbar
const taskbarApp = document.querySelector(`[data-window-id="${windowId}"]`);
if (taskbarApp) {
taskbarApp.remove();
}
// Remove from windows map
this.windows.delete(windowId);
// Focus another window if this was focused
if (this.focusedWindow === windowId) {
const remainingWindows = Array.from(this.windows.keys());
if (remainingWindows.length > 0) {
this.focusWindow(remainingWindows[remainingWindows.length - 1]);
} else {
this.focusedWindow = null;
}
}
}
},
// Taskbar Management
addToTaskbar(windowId, app) {
const taskbar = document.getElementById('taskbar-apps');
const taskbarApp = document.createElement('div');
taskbarApp.className = 'taskbar-app focused';
taskbarApp.dataset.windowId = windowId;
taskbarApp.innerHTML = `
<span class="taskbar-app-icon">${app.icon}</span>
<span class="taskbar-app-name">${app.title}</span>
`;
taskbarApp.addEventListener('click', () => {
const window = this.windows.get(windowId);
if (window) {
if (window.minimized) {
window.element.classList.remove('minimized');
window.minimized = false;
}
this.focusWindow(windowId);
}
});
taskbar.appendChild(taskbarApp);
},
// Dragging functionality
makeDraggable(element, handle) {
let isDragging = false;
let currentX, currentY, initialX, initialY, xOffset = 0, yOffset = 0;
const dragStart = (e) => {
const window = this.windows.get(element.id);
if (window && window.maximized) return; // Can't drag maximized windows
if (e.type === "touchstart") {
initialX = e.touches[0].clientX - xOffset;
initialY = e.touches[0].clientY - yOffset;
} else {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
}
if (e.target === handle || handle.contains(e.target)) {
isDragging = true;
document.body.classList.add('dragging');
}
};
const drag = (e) => {
if (isDragging) {
e.preventDefault();
if (e.type === "touchmove") {
currentX = e.touches[0].clientX - initialX;
currentY = e.touches[0].clientY - initialY;
} else {
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
}
xOffset = currentX;
yOffset = currentY;
element.style.left = currentX + "px";
element.style.top = currentY + "px";
}
};
const dragEnd = () => {
initialX = currentX;
initialY = currentY;
isDragging = false;
document.body.classList.remove('dragging');
};
handle.addEventListener("mousedown", dragStart);
handle.addEventListener("touchstart", dragStart);
document.addEventListener("mousemove", drag);
document.addEventListener("touchmove", drag);
document.addEventListener("mouseup", dragEnd);
document.addEventListener("touchend", dragEnd);
},
// Window resizing functionality
setupWindowResizing(windowId) {
const window = this.windows.get(windowId);
const element = window.element;
const resizeHandles = element.querySelectorAll('.window-resize-handle');
let isResizing = false;
let resizeDirection = '';
let startX, startY, startWidth, startHeight, startLeft, startTop;
const resizeStart = (e) => {
const windowData = this.windows.get(element.id);
if (windowData && windowData.maximized) return; // Can't resize maximized windows
e.preventDefault();
e.stopPropagation();
isResizing = true;
resizeDirection = e.target.dataset.direction;
startX = e.clientX;
startY = e.clientY;
startWidth = parseInt(getComputedStyle(element).width, 10);
startHeight = parseInt(getComputedStyle(element).height, 10);
startLeft = parseInt(getComputedStyle(element).left, 10);
startTop = parseInt(getComputedStyle(element).top, 10);
element.classList.add('resizing');
document.body.style.cursor = e.target.style.cursor;
document.body.style.userSelect = 'none';
};
const resize = (e) => {
if (!isResizing) return;
e.preventDefault();
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
let newWidth = startWidth;
let newHeight = startHeight;
let newLeft = startLeft;
let newTop = startTop;
// Calculate new dimensions based on resize direction
if (resizeDirection.includes('e')) {
newWidth = Math.max(300, startWidth + deltaX);
}
if (resizeDirection.includes('w')) {
newWidth = Math.max(300, startWidth - deltaX);
newLeft = startLeft + (startWidth - newWidth);
}
if (resizeDirection.includes('s')) {
newHeight = Math.max(200, startHeight + deltaY);
}
if (resizeDirection.includes('n')) {
newHeight = Math.max(200, startHeight - deltaY);
newTop = startTop + (startHeight - newHeight);
}
// Apply new dimensions
element.style.width = newWidth + 'px';
element.style.height = newHeight + 'px';
element.style.left = newLeft + 'px';
element.style.top = newTop + 'px';
};
const resizeEnd = () => {
if (!isResizing) return;
isResizing = false;
resizeDirection = '';
element.classList.remove('resizing');
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
// Add event listeners to all resize handles
resizeHandles.forEach(handle => {
handle.addEventListener('mousedown', resizeStart);
});
document.addEventListener('mousemove', resize);
document.addEventListener('mouseup', resizeEnd);
},
// App Creation Functions
createTerminalApp() {
const terminalId = `terminal-${Date.now()}`;
setTimeout(() => {
this.initializeTerminal(terminalId);
}, 100);
return `
<div class="terminal-app" data-terminal-id="${terminalId}">
<div class="terminal-header">
<span class="terminal-title">CatOS Terminal v9.0</span>
<div class="terminal-controls">
<span class="terminal-dot red"></span>
<span class="terminal-dot yellow"></span>
<span class="terminal-dot green"></span>
</div>
</div>
<div class="terminal-content" id="terminal-content-${terminalId}">
<div class="terminal-line">
<span class="terminal-prompt">cat@catos:~$</span>
<span class="terminal-text">Welcome to CatOS Terminal! 🐱</span>
</div>
<div class="terminal-line">
<span class="terminal-prompt">cat@catos:~$</span>
<span class="terminal-text">Type 'help' for available commands or 'meow' for cat wisdom</span>
</div>
</div>
<div class="terminal-input-line">
<span class="terminal-prompt" id="prompt-${terminalId}">cat@catos:~$</span>
<input type="text" class="terminal-input" id="terminal-input-${terminalId}" autofocus>
</div>
</div>
<style>
.terminal-app {
background: #1a1a1a;
color: #00ff00;
font-family: var(--font-mono);
height: 100%;
display: flex;
flex-direction: column;
padding: 0;
}
.terminal-header {
background: #333;
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #555;
}
.terminal-title {
color: #fff;
font-size: 0.875rem;
}
.terminal-controls {
display: flex;
gap: 4px;
}
.terminal-dot {
width: 12px;
height: 12px;
border-radius: 50%;
}
.terminal-dot.red { background: #ff5f56; }
.terminal-dot.yellow { background: #ffbd2e; }
.terminal-dot.green { background: #27ca3f; }
.terminal-content {
flex: 1;
padding: 16px;
overflow-y: auto;
font-size: 0.875rem;
line-height: 1.5;
}
.terminal-line {
margin-bottom: 4px;
}
.terminal-prompt {
color: #00ffff;
margin-right: 8px;
}
.terminal-text {
color: #00ff00;
}
.terminal-input-line {
padding: 16px;
border-top: 1px solid #333;
display: flex;
align-items: center;
}
.terminal-input {
background: transparent;
border: none;
color: #00ff00;
font-family: var(--font-mono);
font-size: 0.875rem;
outline: none;
flex: 1;
margin-left: 8px;
}
.terminal-output {
color: #00ff00;
white-space: pre-line;
}
.terminal-category {
color: #ffff00;
font-weight: bold;
}
.terminal-success {
color: #00ffff;
}
.terminal-directory {
color: #0088ff;
font-weight: bold;
}
.terminal-file {
color: #ff8800;
}
</style>
`;
},
createProjectViewerApp(options) {
const project = this.projects[options.projectId];
if (!project) {
return '<p>Project not found! 🙀</p>';
}
return `
<div class="project-viewer">
<div class="project-header">
<div class="project-icon">${project.icon}</div>
<div class="project-info">
<h2>${project.title}</h2>
<p>${project.description}</p>
</div>
</div>
<div class="project-details">
<div class="project-section">
<h3>🛠️ Technologies</h3>
<div class="tech-tags">
${project.technologies.map(tech => `<span class="tech-tag">${tech}</span>`).join('')}
</div>
</div>
<div class="project-section">
<h3>✨ Features</h3>
<ul class="feature-list">
${project.features.map(feature => `<li>${feature}</li>`).join('')}
</ul>
</div>
<div class="project-actions">
<a href="${project.github}" target="_blank" class="project-btn github-btn">
<i class="fab fa-github"></i> View Code
</a>
<a href="${project.demo}" target="_blank" class="project-btn demo-btn">
<i class="fas fa-external-link-alt"></i> Live Demo
</a>
</div>
</div>
</div>
<style>
.project-viewer {
padding: 0;
}
.project-header {
display: flex;
gap: 16px;
padding: 24px;
border-bottom: 1px solid #e5e7eb;
background: var(--desktop-bg);
color: white;
margin: -24px -24px 24px -24px;
}
.project-icon {
font-size: 3rem;
}
.project-info h2 {
font-size: 1.5rem;
margin-bottom: 8px;
}
.project-details {
padding: 0 24px 24px 24px;
}
.project-section {
margin-bottom: 24px;
}
.project-section h3 {
color: var(--text-primary);
margin-bottom: 12px;
font-size: 1.125rem;
}
.tech-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tech-tag {
background: var(--cat-primary);
color: white;
padding: 4px 12px;
border-radius: 16px;
font-size: 0.875rem;
font-weight: 500;
}
.feature-list {
list-style: none;
padding: 0;
}
.feature-list li {
padding: 8px 0;
border-bottom: 1px solid #f3f4f6;
color: var(--text-secondary);
}
.feature-list li:before {
content: '🐾';
margin-right: 8px;
}
.project-actions {
display: flex;
gap: 12px;
margin-top: 24px;
}
.project-btn {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
border-radius: 8px;
text-decoration: none;
font-weight: 500;
transition: all 0.3s ease;
}
.github-btn {
background: #24292e;
color: white;
}
.demo-btn {
background: var(--cat-primary);
color: white;
}
.project-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
</style>
`;
},
createAboutApp() {
return `
<div class="about-app">
<div class="about-header">
<div class="about-logo">🐱‍💻</div>
<div class="about-info">
<h2>CatOS v9.0 (Whiskers Edition)</h2>
<p>A purr-fessional desktop portfolio experience</p>
</div>
</div>
<div class="system-info">
<div class="info-section">
<h3>System Information</h3>
<div class="info-grid">
<div class="info-item">
<span class="info-label">Developer:</span>
<span class="info-value">Rafael (Certified Cat Whisperer)</span>
</div>
<div class="info-item">
<span class="info-label">Uptime:</span>
<span class="info-value">5+ years coding experience</span>
</div>
<div class="info-item">
<span class="info-label">Memory:</span>
<span class="info-value">∞ GB coffee-powered RAM</span>
</div>
<div class="info-item">
<span class="info-label">Storage:</span>
<span class="info-value">∞ TB of cat photos and code</span>
</div>
</div>
</div>
<div class="info-section">
<h3>Installed Skills</h3>
<div class="skills-list">
<div class="skill-item">
<span class="skill-name">JavaScript</span>
<div class="skill-bar">
<div class="skill-progress" style="width: 95%"></div>
</div>
<span class="skill-level">95%</span>
</div>
<div class="skill-item">
<span class="skill-name">React</span>
<div class="skill-bar">
<div class="skill-progress" style="width: 90%"></div>
</div>
<span class="skill-level">90%</span>
</div>
<div class="skill-item">
<span class="skill-name">Node.js</span>
<div class="skill-bar">
<div class="skill-progress" style="width: 85%"></div>
</div>
<span class="skill-level">85%</span>
</div>
<div class="skill-item">
<span class="skill-name">Cat Psychology</span>
<div class="skill-bar">
<div class="skill-progress" style="width: 100%"></div>
</div>
<span class="skill-level">Expert</span>
</div>
</div>
</div>
</div>
</div>
<style>
.about-app {
padding: 0;
}
.about-header {
display: flex;
gap: 16px;
padding: 24px;
background: var(--desktop-bg);
color: white;
margin: -24px -24px 24px -24px;
}
.about-logo {
font-size: 3rem;
}
.about-info h2 {
font-size: 1.5rem;
margin-bottom: 8px;
}
.system-info {
padding: 0 24px 24px 24px;
}
.info-section {
margin-bottom: 24px;
}
.info-section h3 {
color: var(--text-primary);
margin-bottom: 16px;
font-size: 1.125rem;
}
.info-grid {
display: grid;
gap: 12px;
}
.info-item {
display: flex;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid #f3f4f6;
}
.info-label {
font-weight: 600;
color: var(--text-primary);
}
.info-value {
color: var(--text-secondary);
font-family: var(--font-mono);
}
.skills-list {
display: grid;
gap: 16px;
}
.skill-item {
display: grid;
grid-template-columns: 120px 1fr 60px;
gap: 12px;
align-items: center;
}
.skill-name {
font-weight: 500;
color: var(--text-primary);
}
.skill-bar {
height: 8px;
background: #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
.skill-progress {
height: 100%;
background: var(--cat-primary);
border-radius: 4px;
transition: width 1s ease;
}
.skill-level {
font-size: 0.875rem;
color: var(--text-secondary);
text-align: right;
font-family: var(--font-mono);
}
</style>
`;
},
createContactApp() {
return `
<div class="contact-app">
<div class="contact-header">
<div class="contact-icon">📧</div>
<div class="contact-info">
<h2>Let's Be Cat Friends!</h2>
<p>Got a purr-fect project idea? Want to code together? 🐾</p>
</div>
</div>
<div class="contact-form">
<form id="contact-form">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea id="message" name="message" rows="5" placeholder="Tell me about your project... or just say meow! 😸" required></textarea>
</div>
<button type="submit" class="submit-btn">
<i class="fas fa-paper-plane"></i>
Send Message
</button>
</form>
</div>
<div class="contact-links">
<h3>Other ways to reach me:</h3>
<div class="links-grid">
<a href="mailto:your@email.com" class="contact-link email">
<i class="fas fa-envelope"></i>
<span>Email</span>
</a>
<a href="https://github.com/yourusername" class="contact-link github" target="_blank">
<i class="fab fa-github"></i>
<span>GitHub</span>
</a>
<a href="https://discord.com/users/yourid" class="contact-link discord" target="_blank">
<i class="fab fa-discord"></i>
<span>Discord</span>
</a>
</div>
</div>
</div>
<style>
.contact-app {
padding: 0;
}
.contact-header {
display: flex;
gap: 16px;
padding: 24px;
background: var(--desktop-bg);
color: white;
margin: -24px -24px 24px -24px;
}
.contact-icon {
font-size: 3rem;
}
.contact-info h2 {
font-size: 1.5rem;
margin-bottom: 8px;
}
.contact-form {
padding: 0 24px 24px 24px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-primary);
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px;
border: 1px solid #d1d5db;
border-radius: 8px;
font-family: var(--font-system);
font-size: 0.875rem;
transition: border-color 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--cat-primary);
}
.submit-btn {
background: var(--cat-primary);
color: white;
border: none;
padding: 12px 24px;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: all 0.3s ease;
}
.submit-btn:hover {
background: #5856eb;
transform: translateY(-1px);
}
.contact-links {
padding: 24px;
border-top: 1px solid #e5e7eb;
margin: 0 -24px -24px -24px;
}
.contact-links h3 {
margin-bottom: 16px;
color: var(--text-primary);
}
.links-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
}
.contact-link {
display: flex;
align-items: center;
gap: 8px;
padding: 12px;
border-radius: 8px;
text-decoration: none;
color: white;
font-weight: 500;
transition: transform 0.3s ease;
}
.contact-link:hover {
transform: translateY(-2px);
}
.contact-link.email { background: #ea4335; }
.contact-link.github { background: #24292e; }
.contact-link.discord { background: #5865f2; }
</style>
`;
},
createFileExplorerApp() {
return `
<div class="file-explorer">
<div class="explorer-toolbar">
<div class="breadcrumb">
<span class="breadcrumb-item active">🏠 Home</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-item">catcoder</span>
</div>
<div class="explorer-controls">
<button class="explorer-btn" title="Back">⬅️</button>
<button class="explorer-btn" title="Forward">➡️</button>
<button class="explorer-btn" title="Up">⬆️</button>
</div>
</div>
<div class="explorer-content">
<div class="explorer-sidebar">
<div class="sidebar-section">
<div class="sidebar-item active">
<i class="fas fa-home"></i>
<span>Home</span>
</div>
<div class="sidebar-item">
<i class="fas fa-desktop"></i>
<span>Desktop</span>
</div>
<div class="sidebar-item">
<i class="fas fa-folder"></i>
<span>Projects</span>
</div>
<div class="sidebar-item">
<i class="fas fa-file-alt"></i>
<span>Documents</span>
</div>
<div class="sidebar-item">
<i class="fas fa-image"></i>
<span>Pictures</span>
</div>
</div>
</div>
<div class="explorer-main">
<div class="file-grid">
<div class="file-item folder">
<div class="file-icon">📁</div>
<div class="file-name">Desktop</div>
</div>
<div class="file-item folder">
<div class="file-icon">📁</div>
<div class="file-name">Projects</div>
</div>
<div class="file-item folder">
<div class="file-icon">📁</div>
<div class="file-name">Documents</div>
</div>
<div class="file-item folder">
<div class="file-icon">📁</div>
<div class="file-name">Pictures</div>
</div>
<div class="file-item file">
<div class="file-icon">📄</div>
<div class="file-name">Resume.pdf</div>
</div>
<div class="file-item file">
<div class="file-icon">🐱</div>
<div class="file-name">CatWisdom.txt</div>
</div>
</div>
</div>
</div>
</div>
<style>
.file-explorer {
display: flex;
flex-direction: column;
height: 100%;
padding: 0;
}
.explorer-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #e5e7eb;
background: #f9fafb;
margin: -24px -24px 0 -24px;
}
.breadcrumb {
display: flex;
align-items: center;
font-size: 0.875rem;
color: var(--text-secondary);
}
.breadcrumb-item.active {
color: var(--text-primary);
font-weight: 500;
}
.breadcrumb-separator {
margin: 0 8px;
}
.explorer-controls {
display: flex;
gap: 4px;
}
.explorer-btn {
width: 32px;
height: 32px;
border: 1px solid #d1d5db;
background: white;
border-radius: 4px;
cursor: pointer;
font-size: 0.875rem;
}
.explorer-btn:hover {
background: #f3f4f6;
}
.explorer-content {
display: flex;
flex: 1;
overflow: hidden;
}
.explorer-sidebar {
width: 200px;
background: #f9fafb;
border-right: 1px solid #e5e7eb;
padding: 16px 0;
}
.sidebar-item {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 16px;
cursor: pointer;
font-size: 0.875rem;
color: var(--text-secondary);
transition: all 0.3s ease;
}
.sidebar-item:hover {
background: rgba(99, 102, 241, 0.1);
color: var(--text-primary);
}
.sidebar-item.active {
background: rgba(99, 102, 241, 0.1);
color: var(--cat-primary);
font-weight: 500;
}
.explorer-main {
flex: 1;
padding: 16px;
overflow-y: auto;
}
.file-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 16px;
}
.file-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.file-item:hover {
background: rgba(99, 102, 241, 0.1);
}
.file-icon {
font-size: 2rem;
margin-bottom: 8px;
}
.file-name {
font-size: 0.75rem;
text-align: center;
color: var(--text-primary);
line-height: 1.3;
}
</style>
`;
},
// System functions
updateClock() {
const clockElement = document.querySelector('.time');
const updateTime = () => {
const now = new Date();
const time = now.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
hour12: true
});
clockElement.textContent = time;
};
updateTime();
setInterval(updateTime, 1000);
},
showLoadingScreen() {
const messages = [
'Initializing cat-ware...',
'Loading whiskers.dll...',
'Calibrating purr engine...',
'Installing 9 lives protection...',
'Warming up the litter box...',
'Ready to pounce! 🐾'
];
let messageIndex = 0;
const messageElement = document.querySelector('.loading-message');
const updateMessage = () => {
if (messageIndex < messages.length) {
messageElement.textContent = messages[messageIndex];
messageIndex++;
setTimeout(updateMessage, 500);
}
};
updateMessage();
},
hideLoadingScreen() {
const loadingScreen = document.getElementById('loading-screen');
loadingScreen.classList.add('hidden');
},
playStartupSound() {
// Soft meow sound would go here
console.log('🐱 *gentle startup meow*');
},
showWelcomeMessage() {
this.showNotification('Welcome to CatOS! Double-click icons to launch apps 🐾', 'success');
},
showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.innerHTML = `
<div class="notification-content">
<span class="notification-icon">
${type === 'success' ? '✅' : type === 'error' ? '❌' : 'ℹ️'}
</span>
<span class="notification-message">${message}</span>
</div>
`;
// Add styles
Object.assign(notification.style, {
position: 'fixed',
top: '20px',
right: '20px',
background: type === 'error' ? '#fee2e2' : type === 'success' ? '#d1fae5' : '#dbeafe',
color: type === 'error' ? '#dc2626' : type === 'success' ? '#059669' : '#1d4ed8',
padding: '12px 16px',
borderRadius: '8px',
border: `1px solid ${type === 'error' ? '#fecaca' : type === 'success' ? '#a7f3d0' : '#bfdbfe'}`,
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
zIndex: '10000',
fontSize: '0.875rem',
maxWidth: '300px',
animation: 'slideIn 0.3s ease'
});
document.body.appendChild(notification);
// Auto remove after 3 seconds
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
}, 3000);
},
// Keyboard shortcuts
handleKeyboard(e) {
// Alt + Tab - Switch windows
if (e.altKey && e.key === 'Tab') {
e.preventDefault();
this.switchToNextWindow();
}
// Ctrl + Alt + T - Open terminal
if (e.ctrlKey && e.altKey && e.key.toLowerCase() === 't') {
e.preventDefault();
this.launchApp('terminal');
}
// Escape - Close context menu / start menu
if (e.key === 'Escape') {
this.hideContextMenu();
this.hideStartMenu();
}
},
switchToNextWindow() {
const windowIds = Array.from(this.windows.keys()).filter(id => {
const window = this.windows.get(id);
return !window.minimized;
});
if (windowIds.length === 0) return;
const currentIndex = windowIds.indexOf(this.focusedWindow);
const nextIndex = (currentIndex + 1) % windowIds.length;
this.focusWindow(windowIds[nextIndex]);
},
// Random Wallpaper System
setRandomWallpaper() {
const wallpaperCount = 6; // Update this if you add more wallpapers
const randomWallpaper = Math.floor(Math.random() * wallpaperCount) + 1;
const wallpaperPath = `./static/wallpapers/wallpaper${randomWallpaper}.png`;
// Set the wallpaper as the first background image
const desktop = document.getElementById('desktop');
const currentStyle = getComputedStyle(desktop);
const patternImage = `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="20" opacity="0.1">🐾</text></svg>')`;
desktop.style.backgroundImage = `url('${wallpaperPath}'), ${patternImage}`;
// Log which wallpaper was selected (for debugging)
console.log(`🖼️ Selected wallpaper: wallpaper${randomWallpaper}.png`);
// Add a subtle fade-in effect
desktop.style.opacity = '0';
setTimeout(() => {
desktop.style.transition = 'opacity 0.5s ease';
desktop.style.opacity = '1';
}, 100);
},
// Terminal System
initializeTerminal(terminalId) {
const input = document.getElementById(`terminal-input-${terminalId}`);
const content = document.getElementById(`terminal-content-${terminalId}`);
const prompt = document.getElementById(`prompt-${terminalId}`);
if (!input || !content || !prompt) return;
// Terminal state
const terminal = {
currentDirectory: '/home/catcoder',
commandHistory: [],
historyIndex: -1,
visitorInfo: this.getVisitorInfo()
};
// Handle command input
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const command = input.value.trim();
if (command) {
this.executeCommand(command, terminal, content, prompt);
terminal.commandHistory.push(command);
terminal.historyIndex = terminal.commandHistory.length;
}
input.value = '';
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (terminal.historyIndex > 0) {
terminal.historyIndex--;
input.value = terminal.commandHistory[terminal.historyIndex];
}
} else if (e.key === 'ArrowDown') {
e.preventDefault();
if (terminal.historyIndex < terminal.commandHistory.length - 1) {
terminal.historyIndex++;
input.value = terminal.commandHistory[terminal.historyIndex];
} else {
terminal.historyIndex = terminal.commandHistory.length;
input.value = '';
}
}
});
// Keep input focused
content.addEventListener('click', () => {
input.focus();
});
},
getVisitorInfo() {
return {
browser: navigator.userAgent.split(' ').pop().split('/')[0] || 'Unknown',
platform: navigator.platform || 'Unknown',
language: navigator.language || 'en-US',
screenWidth: screen.width,
screenHeight: screen.height,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'Unknown',
connection: navigator.connection?.effectiveType || 'Unknown',
visitTime: new Date().toISOString()
};
},
executeCommand(command, terminal, content, prompt) {
// Add command to terminal
this.addTerminalLine(content, `${prompt.textContent} ${command}`);
const [cmd, ...args] = command.toLowerCase().split(' ');
const fullArgs = args.join(' ');
// Command routing
switch(cmd) {
case 'help':
this.terminalHelp(content);
break;
case 'clear':
this.terminalClear(content);
break;
case 'whoami':
this.terminalWhoami(content, terminal.visitorInfo);
break;
case 'ls':
this.terminalLs(content, terminal.currentDirectory, fullArgs);
break;
case 'cat':
this.terminalCat(content, fullArgs, terminal.currentDirectory);
break;
case 'cd':
this.terminalCd(content, terminal, fullArgs);
break;
case 'pwd':
this.terminalPwd(content, terminal.currentDirectory);
break;
case 'ps':
this.terminalPs(content, fullArgs);
break;
case 'date':
this.terminalDate(content);
break;
case 'uptime':
this.terminalUptime(content);
break;
case 'meow':
this.terminalMeow(content, fullArgs);
break;
case 'purr':
this.terminalPurr(content);
break;
case 'whiskers':
this.terminalWhiskers(content);
break;
case 'nap':
this.terminalNap(content);
break;
case 'fortune':
this.terminalFortune(content);
break;
case 'curl':
this.terminalCurl(content, fullArgs);
break;
case 'git':
this.terminalGit(content, fullArgs);
break;
case 'npm':
this.terminalNpm(content, fullArgs);
break;
case 'history':
this.terminalHistory(content, terminal.commandHistory);
break;
case 'echo':
this.terminalEcho(content, fullArgs);
break;
default:
this.terminalUnknown(content, cmd);
}
// Update prompt based on directory
this.updatePrompt(prompt, terminal.currentDirectory);
// Scroll to bottom
content.scrollTop = content.scrollHeight;
},
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);
},
updatePrompt(prompt, currentDirectory) {
const dirName = currentDirectory.split('/').pop() || currentDirectory;
const shortDir = dirName === 'catcoder' ? '~' : dirName;
prompt.textContent = `cat@catos:${shortDir}$`;
},
// Terminal Commands
terminalHelp(content) {
const helpText = `
Available Commands:
<span class="terminal-category">📁 Navigation:</span>
ls [path] - List directory contents
cd [directory] - Change directory
pwd - Print working directory
cat [file] - Display file contents
<span class="terminal-category">🔍 System:</span>
whoami - Display visitor information
ps aux - Show running processes
date - Show current date/time
uptime - Show system uptime
history - Show 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
whiskers - ASCII cat art
nap - Activate screensaver
fortune - Cat wisdom
<span class="terminal-category">💡 Tips:</span>
Use ↑/↓ arrows for command history
Try: cat projects/sneaky-cat-proxy.md
`;
this.addTerminalOutput(content, helpText);
},
terminalClear(content) {
content.innerHTML = '';
},
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}
│ Timezone: ${visitorInfo.timezone}
│ Connection: ${visitorInfo.connection}
│ Visit Time: ${new Date(visitorInfo.visitTime).toLocaleString()}
└─────────────────────────────────────┘
<span class="terminal-success">*purrs* Nice to meet you, fellow human! 🐱</span>
`;
this.addTerminalOutput(content, info);
},
terminalLs(content, currentDirectory, args) {
const directories = {
'/home/catcoder': [
{ name: 'Desktop', type: 'dir', icon: '📁' },
{ name: 'projects', type: 'dir', icon: '📁' },
{ name: 'documents', type: 'dir', icon: '📁' },
{ name: 'pictures', type: 'dir', icon: '📁' },
{ name: 'resume.pdf', type: 'file', icon: '📄' },
{ name: 'cat-wisdom.txt', type: 'file', icon: '🐱' }
],
'/home/catcoder/projects': [
{ name: 'sneaky-cat-proxy', type: 'dir', icon: '🔗' },
{ name: 'cat-photo-gallery', type: 'dir', icon: '📸' },
{ name: 'robo-cat-manager', type: 'dir', icon: '🤖' }
],
'/home/catcoder/documents': [
{ name: 'ideas.md', type: 'file', icon: '💡' },
{ name: 'todo.txt', type: 'file', icon: '📝' },
{ name: 'cat-facts.json', type: 'file', icon: '🐾' }
],
'/home/catcoder/pictures': [
{ name: 'profile-cat.jpg', type: 'file', icon: '😸' },
{ name: 'project-screenshots', type: 'dir', icon: '📁' },
{ name: 'memes', type: 'dir', icon: '😹' }
]
};
const contents = directories[currentDirectory] || [];
if (contents.length === 0) {
this.addTerminalOutput(content, 'Directory is empty... like a cat\'s food bowl at 3am 🍽️');
return;
}
let output = `<span class="terminal-category">📂 Contents of ${currentDirectory}:</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);
},
terminalCat(content, filename, currentDirectory) {
const files = {
'resume.pdf': '📄 PDF files need special cat vision! Try opening the About app instead 😸',
'cat-wisdom.txt': `🐱 Cat Wisdom Collection:
• The early cat gets the tuna
• In cat we trust, all others bring treats
• Curiosity didn't kill the cat, it made it a developer
• A cat's work is never done... mostly because we nap too much
• Debugging is like herding cats, but the cats are invisible`,
'ideas.md': `💡 Project Ideas:
- Cat-themed password manager
- Automatic laser pointer for remote work breaks
- AI that detects when treats are needed
- Social network for cats (MeowSpace)
- Smart litter box with analytics dashboard`,
'sneaky-cat-proxy.md': this.getProjectFile('sneaky-cat-proxy'),
'cat-photo-gallery.md': this.getProjectFile('cat-photo-gallery'),
'robo-cat-manager.md': this.getProjectFile('robo-cat-manager')
};
// Handle project files
if (filename.includes('/')) {
const parts = filename.split('/');
const projectName = parts[parts.length - 2];
if (this.projects[projectName]) {
this.addTerminalOutput(content, this.getProjectFile(projectName));
return;
}
}
const fileContent = files[filename];
if (fileContent) {
this.addTerminalOutput(content, fileContent);
} else {
this.addTerminalOutput(content, `cat: ${filename}: No such file or directory 🙀\nTry 'ls' to see available files!`);
}
},
getProjectFile(projectId) {
const project = this.projects[projectId];
if (!project) return 'Project not found! 🙀';
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>
`;
},
terminalCd(content, terminal, directory) {
if (!directory) {
terminal.currentDirectory = '/home/catcoder';
this.addTerminalOutput(content, 'Changed to home directory 🏠');
return;
}
const validDirs = {
'~': '/home/catcoder',
'home': '/home/catcoder',
'projects': '/home/catcoder/projects',
'documents': '/home/catcoder/documents',
'pictures': '/home/catcoder/pictures',
'desktop': '/home/catcoder/Desktop',
'..': '/home/catcoder'
};
if (validDirs[directory]) {
terminal.currentDirectory = validDirs[directory];
this.addTerminalOutput(content, `Changed to ${terminal.currentDirectory} 📁`);
} else {
this.addTerminalOutput(content, `cd: ${directory}: No such directory 🙀`);
}
},
terminalPwd(content, currentDirectory) {
this.addTerminalOutput(content, currentDirectory);
},
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 2048 25.1 12.3 512000 45678 pts/1 R+ 09:31 1:23 /bin/cat-nap-scheduler
cat 4096 5.8 3.2 128000 12345 pts/2 S 09:32 0:15 /usr/bin/treat-dispenser
cat 8192 45.7 25.1 1024000 98765 pts/3 R+ 09:33 2:34 /opt/purr-engine --turbo
cat 1024 2.1 1.8 64000 5432 pts/4 S 09:34 0:08 /bin/whiskers-daemon
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);
},
terminalDate(content) {
const now = new Date();
const catTime = `${now.toLocaleDateString()} ${now.toLocaleTimeString()}`;
this.addTerminalOutput(content, `${catTime} (Cat Standard Time) 🐱⏰`);
},
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 Nap Counter: 42 naps today 😴
Bug Squashing: 99.9% success rate 🐛
Treat Dispenser: Fully operational 🐟
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);
},
terminalMeow(content, message) {
const responses = [
'🐱 *meows back softly*',
'😸 Purrfect! I understand completely!',
'🐾 *headbutts your screen affectionately*',
'😻 That\'s exactly what I was thinking!',
'🙀 You speak fluent cat!',
'😺 *slow blinks* - that means "I love you" in cat',
'🐱‍💻 Meow back! Want to see my latest code?'
];
if (message) {
this.addTerminalOutput(content, `🐱 You said: "${message}"`);
this.addTerminalOutput(content, `🐾 Translation: "Please give me treats"`);
}
const response = responses[Math.floor(Math.random() * responses.length)];
this.addTerminalOutput(content, response);
},
terminalPurr(content) {
this.addTerminalOutput(content, `
🐱 *PURRRRRRRRRRRRRRRR*
😸 Happiness Level: 95% (needs more treats)
🐾 Satisfaction with website: MAXIMUM
😻 Current mood: Content developer cat`);
},
terminalWhiskers(content) {
const catArt = `
/\\_/\\
( o.o )
> ^ <
/\\_/\\ (
( ^.^ ) _) <- This is me coding
o_(")(")
|\\---/|
| o_o | <- Me when code works
\\_^_/
/\\_____/\\
/ o o \\ <- Me reviewing code
( == ^ == )
) (
( )
( ( ) ( ) )
(__(__)___(__)__)
`;
this.addTerminalOutput(content, catArt);
},
terminalNap(content) {
this.addTerminalOutput(content, '😴 Activating cat nap mode...');
setTimeout(() => {
this.addTerminalOutput(content, '🐱 *stretches and yawns*');
setTimeout(() => {
this.addTerminalOutput(content, '😸 Refreshed and ready to code! *tail swish*');
}, 2000);
}, 2000);
},
terminalFortune(content) {
const fortunes = [
'🐱 A cat\'s code is worth a thousand barks.',
'😸 Today is a good day to push to production.',
'🐾 Your code will run purrfectly... eventually.',
'😻 The best debugging happens at 2 AM with a cat on your keyboard.',
'🙀 Error 404: Treats not found. Please refill immediately.',
'😺 In the future, all websites will be operated by cats.',
'🐱‍💻 Curiosity didn\'t kill the cat; it made it a senior developer.',
'😹 Your next commit will be legendary... like a cat video.',
'🐾 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}`);
},
terminalCurl(content, url) {
if (!url) {
this.addTerminalOutput(content, 'curl: no URL specified 🙀\nUsage: curl <url>');
return;
}
this.addTerminalOutput(content, `🌐 Fetching ${url}...`);
setTimeout(() => {
if (url.includes('cat') || url.includes('meow')) {
this.addTerminalOutput(content, `😸 Connection successful! Cat-approved website detected.`);
} else if (url.includes('dog')) {
this.addTerminalOutput(content, `🙀 Warning: Canine content detected. Proceed with caution.`);
} else {
this.addTerminalOutput(content, `📡 HTTP/1.1 200 OK - Site looks paw-some!`);
}
}, 1500);
},
terminalGit(content, args) {
if (args.startsWith('status')) {
this.addTerminalOutput(content, `
On branch main
Your branch is up to date with 'origin/main'
Changes not staged for commit:
modified: life.js (added more cat puns)
modified: happiness.css (increased by 200%)
Untracked files:
cat-treats.json
purr-sounds.wav
😸 Working tree status: Purrfect!`);
} else if (args.startsWith('log')) {
this.addTerminalOutput(content, `
commit a1b2c3d (HEAD -> main)
Author: CatDeveloper <meow@catos.dev>
Date: ${new Date().toDateString()}
Fix: Resolved all bugs with strategic cat napping
commit d4e5f6g
Author: CatDeveloper <meow@catos.dev>
Date: Yesterday
Add: More cat puns to error messages`);
} else {
this.addTerminalOutput(content, '🐱 Git command executed! *purrs approvingly*');
}
},
terminalNpm(content, args) {
if (args.startsWith('install')) {
this.addTerminalOutput(content, `
📦 Installing cat-packages...
🐾 + cat-utils@9.0.0
😸 + purr-framework@1.2.3
🐱 + meow-validator@0.5.7
😻 + treat-dispenser@2.1.0
added 42 packages in 3.14s (purr time)
😺 All packages installed successfully!`);
} else if (args.startsWith('run')) {
this.addTerminalOutput(content, `🏃‍♀️ Running npm script... *cat runs in circles*`);
} else {
this.addTerminalOutput(content, '📦 NPM operation completed! Dependencies are purr-fect! 😸');
}
},
terminalHistory(content, commandHistory) {
if (commandHistory.length === 0) {
this.addTerminalOutput(content, 'History is empty... like a cat\'s promise to stay off the keyboard 😸');
return;
}
let output = '<span class="terminal-category">📚 Command History:</span>\n';
commandHistory.forEach((cmd, index) => {
output += `${index + 1}. ${cmd}\n`;
});
this.addTerminalOutput(content, output);
},
terminalEcho(content, text) {
if (!text) {
this.addTerminalOutput(content, '');
return;
}
if (text.toLowerCase().includes('cat') || text.toLowerCase().includes('meow')) {
this.addTerminalOutput(content, `${text} 😸`);
} else {
this.addTerminalOutput(content, text);
}
},
terminalUnknown(content, command) {
const suggestions = [
`🙀 Command '${command}' not found! Did you mean to meow instead?`,
`😿 '${command}' is not a valid command. Try 'help' for available commands!`,
`🐱 Unknown command '${command}'. Even cats make typos! Try 'help'.`,
`😸 '${command}'? That's not cat language! Type 'help' to see what I understand.`,
`🐾 Command '${command}' not recognized. Are you sure you're not a dog? 🐕`
];
const suggestion = suggestions[Math.floor(Math.random() * suggestions.length)];
this.addTerminalOutput(content, suggestion);
}
};
// Initialize CatOS when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
CatOS.init();
});
// Add CSS animations
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
.notification-content {
display: flex;
align-items: center;
gap: 8px;
}
`;
document.head.appendChild(style);
console.log('🐱‍💻 CatOS v9.0 (Whiskers Edition) loaded successfully!');
console.log('💡 Try these keyboard shortcuts:');
console.log(' • Alt + Tab: Switch windows');
console.log(' • Ctrl + Alt + T: Open terminal');
console.log(' • Escape: Close menus');