if (typeof window.WindowManager === "undefined") { class WindowManager { constructor() { this.openWindows = []; this.activeWindowId = null; this.zIndexCounter = 100; // Will fetch dynamically in open() since script runs before DOM is ready this.workspace = null; this.taskbarApps = null; } open(id, title, htmlContent) { // Lazy load the container elements to avoid head script loading issues if (!this.workspace) this.workspace = document.getElementById("desktop-content") || document.body; if (!this.taskbarApps) this.taskbarApps = document.getElementById("taskbar-apps"); // If window already exists, focus it const existingWindow = this.openWindows.find((w) => w.id === id); if (existingWindow) { this.focus(id); return; } // Create new window const windowData = { id, title, isMinimized: false, isMaximized: false, previousState: null, }; this.openWindows.push(windowData); // Generate DOM structure const windowEl = document.createElement("div"); windowEl.id = `window-${id}`; // Add random slight offset for cascade effect const offset = (this.openWindows.length * 20) % 100; const top = 100 + offset; const left = 150 + offset; windowEl.className = "window-element"; windowEl.style.top = `${top}px`; windowEl.style.left = `${left}px`; windowEl.style.zIndex = this.zIndexCounter++; windowEl.innerHTML = `
${title}
`; this.workspace.appendChild(windowEl); // Inject content into the window body const windowBody = windowEl.querySelector(`#window-body-${id}`); if (windowBody) { this.injectContentWithScripts(windowBody, htmlContent); } // Add to taskbar if (this.taskbarApps) { const taskbarIcon = document.createElement("div"); taskbarIcon.id = `taskbar-item-${id}`; taskbarIcon.className = "taskbar-item taskbar-icon"; taskbarIcon.onclick = () => this.toggleMinimize(id); let iconHtml = ''; if (id === "vibe") iconHtml = ''; else if (id === "tasks") iconHtml = ''; else if (id === "chat") iconHtml = ''; else if (id === "terminal") iconHtml = ''; else if (id === "drive") iconHtml = ''; else if (id === "editor") iconHtml = ''; else if (id === "browser") iconHtml = ''; else if (id === "mail") iconHtml = ''; else if (id === "settings") iconHtml = ''; taskbarIcon.innerHTML = `
${iconHtml}
`; this.taskbarApps.appendChild(taskbarIcon); } this.makeDraggable(windowEl); this.makeResizable(windowEl); this.focus(id); // Tell HTMX to process the new content if (window.htmx) { htmx.process(windowEl); } } focus(id) { this.activeWindowId = id; const windowEl = document.getElementById(`window-${id}`); if (windowEl) { windowEl.style.zIndex = this.zIndexCounter++; } // Highlight taskbar icon if (this.taskbarApps) { const icons = this.taskbarApps.querySelectorAll(".taskbar-icon"); icons.forEach((icon) => { icon.classList.add("border-transparent"); }); const activeIcon = document.getElementById(`taskbar-item-${id}`); if (activeIcon) { activeIcon.classList.remove("border-transparent"); } } } close(id) { const windowEl = document.getElementById(`window-${id}`); if (windowEl) { windowEl.remove(); } const taskbarIcon = document.getElementById(`taskbar-item-${id}`); if (taskbarIcon) { taskbarIcon.remove(); } this.openWindows = this.openWindows.filter((w) => w.id !== id); if (this.activeWindowId === id) { this.activeWindowId = null; // Optionally focus the next highest z-index window } } toggleMinimize(id) { const windowObj = this.openWindows.find((w) => w.id === id); if (!windowObj) return; const windowEl = document.getElementById(`window-${id}`); if (!windowEl) return; if (windowObj.isMinimized) { // Restore windowEl.style.display = "flex"; windowObj.isMinimized = false; this.focus(id); } else { // Minimize windowEl.style.display = "none"; windowObj.isMinimized = true; if (this.activeWindowId === id) { this.activeWindowId = null; } } } toggleMaximize(id) { const windowObj = this.openWindows.find((w) => w.id === id); if (!windowObj) return; const windowEl = document.getElementById(`window-${id}`); if (!windowEl) return; if (windowObj.isMaximized) { // Restore windowEl.style.width = windowObj.previousState.width; windowEl.style.height = windowObj.previousState.height; windowEl.style.top = windowObj.previousState.top; windowEl.style.left = windowObj.previousState.left; windowObj.isMaximized = false; } else { // Maximize windowObj.previousState = { width: windowEl.style.width, height: windowEl.style.height, top: windowEl.style.top, left: windowEl.style.left, }; // Adjust for taskbar height (assuming taskbar is at bottom) const taskbarHeight = document.getElementById("taskbar") ? document.getElementById("taskbar").offsetHeight : 0; windowEl.style.width = "100%"; windowEl.style.height = `calc(100% - ${taskbarHeight}px)`; windowEl.style.top = "0px"; windowEl.style.left = "0px"; windowObj.isMaximized = true; } this.focus(id); } makeDraggable(windowEl) { const header = windowEl.querySelector(".window-header"); if (!header) return; let isDragging = false; let startX, startY, initialLeft, initialTop; const onMouseDown = (e) => { // Don't drag if clicking buttons if ( e.target.tagName.toLowerCase() === "button" || e.target.closest("button") ) return; isDragging = true; startX = e.clientX; startY = e.clientY; initialLeft = parseInt(windowEl.style.left || 0, 10); initialTop = parseInt(windowEl.style.top || 0, 10); this.focus(windowEl.id.replace("window-", "")); document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); }; const onMouseMove = (e) => { if (!isDragging) return; // Allow animation frame optimization here in a real implementation requestAnimationFrame(() => { const dx = e.clientX - startX; const dy = e.clientY - startY; // Add basic boundaries let newLeft = initialLeft + dx; let newTop = initialTop + dy; // Prevent dragging completely out newTop = Math.max(0, newTop); windowEl.style.left = `${newLeft}px`; windowEl.style.top = `${newTop}px`; }); }; const onMouseUp = () => { isDragging = false; document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); }; header.addEventListener("mousedown", onMouseDown); // Add focus listener to the whole window windowEl.addEventListener("mousedown", () => { this.focus(windowEl.id.replace("window-", "")); }); } injectContentWithScripts(container, htmlContent) { // Create a temporary div to parse the HTML const tempDiv = document.createElement("div"); tempDiv.innerHTML = htmlContent; // Extract all script tags const scripts = tempDiv.querySelectorAll("script"); const scriptsToExecute = []; scripts.forEach((originalScript) => { const scriptClone = document.createElement("script"); Array.from(originalScript.attributes).forEach((attr) => { scriptClone.setAttribute(attr.name, attr.value); }); scriptClone.textContent = originalScript.textContent; scriptsToExecute.push(scriptClone); originalScript.remove(); // Remove from tempDiv so innerHTML doesn't include it }); // Inject HTML content without scripts container.innerHTML = tempDiv.innerHTML; // Execute each script scriptsToExecute.forEach((script) => { container.appendChild(script); }); } makeResizable(windowEl) { // Implement simple bottom-right resize for now // In a full implementation, you'd add invisible handles windowEl.style.resize = "both"; // Note: CSS resize creates conflicts with custom dragging/resizing if not careful. // For a true "WinBox" feel, custom handles (divs) on all 8 edges/corners are needed. } } // Initialize globally window.WindowManager = new WindowManager(); }