mirror of
https://github.com/ghndrx/webos.git
synced 2026-02-10 06:45:00 +00:00
306 lines
11 KiB
JavaScript
306 lines
11 KiB
JavaScript
// 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('SysOps/DevOps Engineer — wears all the hats');
|
|
addLine('AWS | Security | EKS | AI-augmented workflows');
|
|
},
|
|
projects: () => {
|
|
addLine('Projects:');
|
|
addLine(' - AWS Cost Optimization / FinOps');
|
|
addLine(' - Platform Engineering (EKS + Karpenter)');
|
|
addLine(' - DevSecOps Pipelines');
|
|
addLine(' - Observability Stack');
|
|
addLine(' - AI-Assisted Automation');
|
|
addLine(' - Terraform Modules');
|
|
},
|
|
skills: () => {
|
|
addLine('Skills: AWS, Terraform, Kubernetes, ArgoCD, GitOps, FinOps,');
|
|
addLine(' DevSecOps, Platform Engineering, Python, Go, AI/Automation');
|
|
},
|
|
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();
|
|
});
|