// GregOS - WebOS Resume Site class GregOS { constructor() { this.windows = new Map(); this.zIndex = 100; this.windowOffset = { x: 50, y: 50 }; this.init(); } init() { this.bindIconClicks(); this.startClock(); this.setupTerminal(); } bindIconClicks() { document.querySelectorAll('.icon').forEach(icon => { icon.addEventListener('dblclick', () => { const windowId = icon.dataset.window; this.openWindow(windowId); }); // Mobile: single tap icon.addEventListener('click', (e) => { if (window.innerWidth <= 768) { const windowId = icon.dataset.window; this.openWindow(windowId); } }); }); } openWindow(id) { if (this.windows.has(id)) { const win = this.windows.get(id); win.element.classList.remove('minimized'); this.focusWindow(win.element); return; } const template = document.getElementById('window-template'); const content = document.getElementById(`content-${id}`); if (!template || !content) return; const windowEl = template.content.cloneNode(true).querySelector('.window'); const title = document.querySelector(`[data-window="${id}"] span`).textContent; windowEl.querySelector('.window-title').textContent = title; windowEl.querySelector('.window-content').innerHTML = content.innerHTML; // If terminal, add terminal class if (id === 'terminal') { windowEl.querySelector('.window-content').classList.add('terminal-content'); } // Position window windowEl.style.left = this.windowOffset.x + 'px'; windowEl.style.top = this.windowOffset.y + 'px'; windowEl.style.width = '500px'; windowEl.style.height = '400px'; this.windowOffset.x += 30; this.windowOffset.y += 30; if (this.windowOffset.x > 300) this.windowOffset = { x: 50, y: 50 }; document.querySelector('.windows-container').appendChild(windowEl); this.windows.set(id, { element: windowEl, title }); this.focusWindow(windowEl); this.addTaskbarItem(id, title); this.setupWindowControls(windowEl, id); this.setupDrag(windowEl); this.setupResize(windowEl); // Focus terminal input if (id === 'terminal') { setTimeout(() => { const input = windowEl.querySelector('#terminal-input'); if (input) input.focus(); }, 100); } } setupWindowControls(windowEl, id) { windowEl.querySelector('.close').addEventListener('click', () => { windowEl.remove(); this.windows.delete(id); this.removeTaskbarItem(id); }); windowEl.querySelector('.minimize').addEventListener('click', () => { windowEl.classList.add('minimized'); }); windowEl.querySelector('.maximize').addEventListener('click', () => { if (windowEl.dataset.maximized === 'true') { windowEl.style.left = windowEl.dataset.prevLeft; windowEl.style.top = windowEl.dataset.prevTop; windowEl.style.width = windowEl.dataset.prevWidth; windowEl.style.height = windowEl.dataset.prevHeight; windowEl.dataset.maximized = 'false'; } else { windowEl.dataset.prevLeft = windowEl.style.left; windowEl.dataset.prevTop = windowEl.style.top; windowEl.dataset.prevWidth = windowEl.style.width; windowEl.dataset.prevHeight = windowEl.style.height; windowEl.style.left = '0'; windowEl.style.top = '0'; windowEl.style.width = '100%'; windowEl.style.height = 'calc(100vh - 50px)'; windowEl.dataset.maximized = 'true'; } }); windowEl.addEventListener('mousedown', () => this.focusWindow(windowEl)); } focusWindow(windowEl) { this.zIndex++; windowEl.style.zIndex = this.zIndex; } setupDrag(windowEl) { const header = windowEl.querySelector('.window-header'); let isDragging = false; let startX, startY, startLeft, startTop; header.addEventListener('mousedown', (e) => { if (e.target.closest('.window-controls')) return; isDragging = true; startX = e.clientX; startY = e.clientY; startLeft = parseInt(windowEl.style.left) || 0; startTop = parseInt(windowEl.style.top) || 0; windowEl.style.transition = 'none'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const dx = e.clientX - startX; const dy = e.clientY - startY; windowEl.style.left = startLeft + dx + 'px'; windowEl.style.top = startTop + dy + 'px'; }); document.addEventListener('mouseup', () => { isDragging = false; windowEl.style.transition = ''; }); } setupResize(windowEl) { let isResizing = false; let startX, startY, startWidth, startHeight; windowEl.addEventListener('mousedown', (e) => { const rect = windowEl.getBoundingClientRect(); if (e.clientX > rect.right - 15 && e.clientY > rect.bottom - 15) { isResizing = true; startX = e.clientX; startY = e.clientY; startWidth = rect.width; startHeight = rect.height; e.preventDefault(); } }); document.addEventListener('mousemove', (e) => { if (!isResizing) return; const dx = e.clientX - startX; const dy = e.clientY - startY; windowEl.style.width = Math.max(400, startWidth + dx) + 'px'; windowEl.style.height = Math.max(300, startHeight + dy) + 'px'; }); document.addEventListener('mouseup', () => { isResizing = false; }); } addTaskbarItem(id, title) { const item = document.createElement('div'); item.className = 'taskbar-item'; item.dataset.window = id; item.textContent = title; item.addEventListener('click', () => { const win = this.windows.get(id); if (win) { win.element.classList.toggle('minimized'); if (!win.element.classList.contains('minimized')) { this.focusWindow(win.element); } } }); document.querySelector('.taskbar-items').appendChild(item); } removeTaskbarItem(id) { const item = document.querySelector(`.taskbar-item[data-window="${id}"]`); if (item) item.remove(); } startClock() { const updateClock = () => { const now = new Date(); const time = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); document.querySelector('.clock').textContent = time; }; updateClock(); setInterval(updateClock, 1000); } setupTerminal() { document.addEventListener('keydown', (e) => { const input = document.getElementById('terminal-input'); if (!input || document.activeElement !== input) return; if (e.key === 'Enter') { this.executeCommand(input.value); input.value = ''; } }); } executeCommand(cmd) { const output = document.getElementById('terminal-output'); if (!output) return; const addLine = (text, color = '#0f0') => { const p = document.createElement('p'); p.textContent = text; p.style.color = color; output.appendChild(p); output.scrollTop = output.scrollHeight; }; addLine(`guest@gregos:~$ ${cmd}`); const commands = { help: () => { addLine('Available commands:'); addLine(' help - Show this help'); addLine(' about - About me'); addLine(' projects - List projects'); addLine(' skills - Show skills'); addLine(' contact - Contact info'); addLine(' clear - Clear terminal'); addLine(' neofetch - System info'); }, about: () => { addLine('Greg Hendrickson'); addLine('Developer, tinkerer, builder of things.'); }, projects: () => { addLine('Projects:'); addLine(' - ShellMate (shellmate.sh)'); addLine(' - Clawdbot'); }, skills: () => { addLine('Skills: Python, TypeScript, Go, Linux, Docker, AI/ML'); }, contact: () => { addLine('GitHub: github.com/ghndrx'); }, clear: () => { output.innerHTML = ''; }, neofetch: () => { addLine(' ____ ___ ____ ', '#e94560'); addLine(' / ___|_ __ ___ __ _ / _ \\/ ___| ', '#e94560'); addLine(' | | _| \'__/ _ \\/ _` | | | \\___ \\ ', '#e94560'); addLine(' | |_| | | | __/ (_| | |_| |___) |', '#e94560'); addLine(' \\____|_| \\___|\\__, |\\___/|____/ ', '#e94560'); addLine(' |___/ ', '#e94560'); addLine(''); addLine('OS: GregOS v1.0'); addLine('Host: The Internet'); addLine('Uptime: Since the early days'); addLine('Shell: bash'); addLine('Terminal: WebTerminal'); } }; const trimmedCmd = cmd.trim().toLowerCase(); if (trimmedCmd === '') return; if (commands[trimmedCmd]) { commands[trimmedCmd](); } else { addLine(`Command not found: ${cmd}. Type 'help' for available commands.`, '#ff6b6b'); } } } // Initialize document.addEventListener('DOMContentLoaded', () => { window.gregos = new GregOS(); });