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();
}