commit 55e1616e1b7601d2ae6b2c72c6fa0055399fff73 Author: usernames122 Date: Sat Aug 9 05:25:35 2025 +0200 first commit diff --git a/clickin.wav b/clickin.wav new file mode 100644 index 0000000..0204d5f Binary files /dev/null and b/clickin.wav differ diff --git a/clickout.wav b/clickout.wav new file mode 100644 index 0000000..23096ae Binary files /dev/null and b/clickout.wav differ diff --git a/immersion.js b/immersion.js new file mode 100644 index 0000000..a366381 --- /dev/null +++ b/immersion.js @@ -0,0 +1,49 @@ +// immersion.js +(function () { + // Preload audio + const clickIn = new Audio('clickin.wav'); + const clickOut = new Audio('clickout.wav'); + + // Optional: make sure they can play multiple times quickly + clickIn.preload = 'auto'; + clickOut.preload = 'auto'; + + document.addEventListener('mousedown', () => { + // Reset currentTime so rapid clicks restart the sound + clickIn.currentTime = 0; + clickIn.play().catch(err => { + // Ignore if browser blocks autoplay until user interaction + console.warn('Click-in sound not played:', err); + }); + }); + + document.addEventListener('mouseup', () => { + clickOut.currentTime = 0; + clickOut.play().catch(err => { + console.warn('Click-out sound not played:', err); + }); + }); + windowHooks.push(function (e) { + if (e.event === "windowCreate") { + const iframe = e.window.iframe; + iframe.onload = function () { + try { + // Access the iframe's global scope + iframe.contentWindow.clickIn = clickIn; + iframe.contentWindow.clickOut = clickOut; + + // Also attach event listeners inside iframe + const doc = iframe.contentDocument; + doc.addEventListener('mousedown', () => { + clickIn.currentTime = 0; + clickIn.play().catch(() => { }); + }); + doc.addEventListener('mouseup', () => { + clickOut.currentTime = 0; + clickOut.play().catch(() => { }); + }); + } catch { }; + }; + } + }); +})(); diff --git a/index.html b/index.html new file mode 100644 index 0000000..2b18dd6 --- /dev/null +++ b/index.html @@ -0,0 +1,23 @@ + + + + + + + LAMINAX Windower + + + + +
+
+ + + + + + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..fbd78dc --- /dev/null +++ b/script.js @@ -0,0 +1,303 @@ +// WINDOW MANAGER +const desktop = document.getElementById('desktop'); +const bglayer = document.getElementById('background-layer'); +const newWindowBtn = document.getElementById('new-window-btn'); +let zIndexCounter = 1; + +let focusedWindow = null; +let windows = {}; +let windowHooks = []; + +function uniqueid() { + // always start with a letter (for DOM friendlyness) + var idstr = String.fromCharCode(Math.floor((Math.random() * 25) + 65)); + do { + // between numbers and characters (48 is 0 and 90 is Z (42-48 = 90) + var ascicode = Math.floor((Math.random() * 42) + 48); + if (ascicode < 58 || ascicode > 64) { + // exclude all chars between : (58) and @ (64) + idstr += String.fromCharCode(ascicode); + } + } while (idstr.length < 32); + + return (idstr); +} + +function fadeOut(fadeTarget, speed, callback) { + let eee = 0; + var fadeEffect = setInterval(function () { + if (!fadeTarget.style.opacity) { + fadeTarget.style.opacity = 1; + } + fadeTarget.style.transform = `rotate3d(1, 0, 0, ${eee}deg)`; + if (fadeTarget.style.opacity > 0) { + fadeTarget.style.opacity -= 0.1; + eee += 10; + } else { + clearInterval(fadeEffect); + callback(); + } + }, speed); +} + +function killWind(id) { + const windowDiv = windows[id]?.windElems?.windowDiv; + if (typeof (windowDiv) == "undefined") { + console.warn("Attempted to close undefined window.."); + return; + }; + fadeOut(windowDiv, 20, function () { + windowDiv.remove(); + delete windows[id]; + windowHooks.forEach(element => { + element({ event: "windowDestroy", id: id }) + }); + }); +} + +function focusWindow(id) { + const windData = windows[id]; + const windowDiv = windData?.windElems?.windowDiv; + if (typeof (windData) == "undefined") { + console.warn("Attempted to focus undefined window.."); + return; + }; + windowDiv.style.zIndex = zIndexCounter++; + if (windData.layer == "background") windowDiv.style.zIndex = windowDiv.style.zIndex * -1; + windowHooks.forEach(element => { + element({ event: "windowFocused", id: id }) + }); + Object.keys(windows).forEach(key => { + const element = windows[key]; + if (element.alwaysOnTop) { + element.windElems.windowDiv.style.zIndex = zIndexCounter + 5; + } + }); + focusedWindow = id; +} + +function createWindow(url, settings) { + const windID = uniqueid(); + const name = settings.name; + const headerless = settings.headerless || false; + const resizable = settings.resizable !== undefined ? settings.resizable : true; + const shouldIndent = settings.indent !== undefined ? settings.indent : true; + const layer = settings.layer || "foreground"; + + const windowDiv = document.createElement('div'); + windowDiv.classList.add('window'); + windowDiv.style.top = '50px'; + if (shouldIndent) windowDiv.style.left = '50px'; + windowDiv.style.zIndex = zIndexCounter++; + windowDiv.id = windID; + + // Header + const header = document.createElement('div'); + header.classList.add('window-header'); + + if (headerless) header.classList.add("window-hidden"); + + const title = document.createElement('div'); + title.classList.add('window-title'); + title.textContent = name || url; + + const closeBtn = document.createElement('button'); + closeBtn.classList.add('window-close-btn'); + closeBtn.textContent = '×'; + + header.appendChild(title); + header.appendChild(closeBtn); + windowDiv.appendChild(header); + + // iframe + const iframe = document.createElement('iframe'); + iframe.classList.add('window-iframe'); + iframe.src = url; + windowDiv.appendChild(iframe); + iframe.onload = function () { + try { + iframe.contentDocument.oncontextmenu = function () { + return false; + }; + } catch { }; + }; + + // Add resize handle if resizable + let resizeHandle = null; + if (resizable) { + resizeHandle = document.createElement('div'); + resizeHandle.style.width = '16px'; + resizeHandle.style.height = '16px'; + resizeHandle.style.position = 'absolute'; + resizeHandle.style.right = '0'; + resizeHandle.style.bottom = '0'; + resizeHandle.style.cursor = 'se-resize'; + // Optional: make it visible if you want + // resizeHandle.style.background = 'rgba(0,0,0,0.2)'; + windowDiv.appendChild(resizeHandle); + } + + if (layer == "foreground") desktop.appendChild(windowDiv); + else bglayer.appendChild(windowDiv); + + // Dragging functionality + let isDragging = false; + let offsetX, offsetY; + + header.addEventListener('mousedown', (e) => { + isDragging = true; + offsetX = e.clientX - windowDiv.getBoundingClientRect().left; + offsetY = e.clientY - windowDiv.getBoundingClientRect().top; + focusWindow(windID); + }); + + // Resizing variables + let isResizing = false; + let startWidth, startHeight, startX, startY; + + window.addEventListener('mouseup', () => { + isDragging = false; + isResizing = false; + }); + + window.addEventListener('mousemove', (e) => { + if (isDragging) { + let newX = e.clientX - offsetX; + let newY = e.clientY - offsetY; + + transformWindow(windID, null, null, newX, newY); + + + } else if (isResizing) { + const dx = e.clientX - startX; + const dy = e.clientY - startY; + + const newWidth = startWidth + dx; + const newHeight = startHeight + dy; + + transformWindow(windID, newWidth, newHeight); + } + }); + + // Resize handle mousedown + if (resizeHandle) { + resizeHandle.addEventListener('mousedown', (e) => { + e.stopPropagation(); + isResizing = true; + startWidth = windowDiv.offsetWidth; + startHeight = windowDiv.offsetHeight; + startX = e.clientX; + startY = e.clientY; + focusWindow(windID); + }); + } + + // Focus window on click + windowDiv.addEventListener('mousedown', () => { + focusWindow(windID); + }); + + + + // Close window + closeBtn.addEventListener('click', () => { + killWind(windID); + }); + + const windObj = { + iframe: iframe, + windElems: { + closeBtn: closeBtn, + windowDiv: windowDiv, + header: header, + title: title, + resizeHandle: resizeHandle + }, + windID: windID, + minSizeX: 250, + minSizeY: 150, + alwaysOnTop: false, + layer: layer, + hideInTaskbar: layer == "background" + }; + + windows[windID] = windObj; + windowHooks.forEach(element => { + element({ event: "windowCreate", id: windID, window: windObj }) + }); + return windObj; +} + + +function transformWindow(id, sizex, sizey, posx, posy, ignoreRects) { + const windData = windows[id]; + const windowDiv = windData?.windElems?.windowDiv; + if (typeof (windData) == "undefined") { + console.warn("Attempted to transform undefined window.."); + return; + }; + if (sizex) windowDiv.style.width = Math.max(windData.minSizeX, sizex) + 'px'; + if (sizey) windowDiv.style.height = Math.max(windData.minSizeY, sizey) + 'px'; + const desktopRect = desktop.getBoundingClientRect(); + const winRect = windowDiv.getBoundingClientRect(); + if (posx) { + const newX = ignoreRects ? posx : Math.max(desktopRect.left, Math.min(posx, desktopRect.right - winRect.width)); + windowDiv.style.left = newX - desktopRect.left + 'px'; + } + if (posy) { + const newY = ignoreRects ? posy : Math.max(desktopRect.top, Math.min(posy, desktopRect.bottom - winRect.height)); + windowDiv.style.top = newY - desktopRect.top + 'px'; + } + +} + +function getTransform(id) { + const windData = windows[id]; + const windowDiv = windData?.windElems?.windowDiv; + if (typeof windData === "undefined" || !windowDiv) { + console.warn("Attempted to transform undefined window.."); + return; + } + + const desktopRect = desktop.getBoundingClientRect(); + + // Extract numeric values from style or fallback to computed styles + const style = windowDiv.style; + + // Helper to parse px values safely + function parsePx(val) { + if (!val) return 0; + return Math.floor(parseFloat(val)); + } + + // Use inline style if present, otherwise computed style + let topStr = style.top || window.getComputedStyle(windowDiv).top; + let leftStr = style.left || window.getComputedStyle(windowDiv).left; + let widthStr = style.width || window.getComputedStyle(windowDiv).width; + let heightStr = style.height || window.getComputedStyle(windowDiv).height; + + // Parse numbers from strings like "50px" + const posy = parsePx(topStr); + const posx = parsePx(leftStr); + const sizex = parsePx(widthStr); + const sizey = parsePx(heightStr); + + return { + x: posx, + y: posy, + sizex: sizex, + sizey: sizey + }; +} + + +function updateWindowMin(id) { + const windData = windows[id]; + if (typeof (windData) == "undefined") { + console.warn("Attempted to update undefined window.."); + return; + }; + const transform = getTransform(id); + transformWindow(id,transform.sizex,transform.sizey,transform.posx,transform.posy); + +} \ No newline at end of file diff --git a/shell.js b/shell.js new file mode 100644 index 0000000..872cdda --- /dev/null +++ b/shell.js @@ -0,0 +1,286 @@ +var embeddedApps = { + calc: `data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+PGh0bWwgbGFuZz0iZW4iPjxoZWFkPjxtZXRhIGNoYXJzZXQ9IlVURi04Ij48dGl0bGU+Q2FsY3VsYXRvcjwvdGl0bGU+PHN0eWxlPmJvZHl7bWFyZ2luOjA7Zm9udC1mYW1pbHk6bW9ub3NwYWNlLG1vbm9zcGFjZTtkaXNwbGF5OmZsZXg7anVzdGlmeS1jb250ZW50OmNlbnRlcjthbGlnbi1pdGVtczpjZW50ZXI7aGVpZ2h0OjEwMHZoO2JhY2tncm91bmQ6IzIyMjtjb2xvcjojZWVlfSNjYWxjdWxhdG9ye2JhY2tncm91bmQ6IzMzMztwYWRkaW5nOjFyZW07Ym9yZGVyLXJhZGl1czo4cHg7d2lkdGg6MjIwcHg7Ym94LXNoYWRvdzowIDAgMTBweCAjMDAwfSNkaXNwbGF5e3dpZHRoOjEwMCU7aGVpZ2h0OjQwcHg7Zm9udC1zaXplOjEuMnJlbTtiYWNrZ3JvdW5kOiMxMTE7Ym9yZGVyOm5vbmU7Y29sb3I6I2VlZTtwYWRkaW5nOjAgMDttYXJnaW4tYm90dG9tOjEwcHg7dGV4dC1hbGlnbjpyaWdodH1idXR0b257d2lkdGg6NDhweDtoZWlnaHQ6NDhweDttYXJnaW46MnB4O2ZvbnQtc2l6ZToxLjJyZW07Ym9yZGVyOm5vbmU7Ym9yZGVyLXJhZGl1czo0cHg7Y3Vyc29yOnBvaW50ZXI7YmFja2dyb3VuZDojNTU1O2NvbG9yOiNlZWU7dHJhbnNpdGlvbjpiYWNrZ3JvdW5kIC4ycyBlYXNlfWJ1dHRvbjpob3ZlcntiYWNrZ3JvdW5kOiM3Nzd9Lm9wZXJhdG9ye2JhY2tncm91bmQ6I2Y1N2MwMDtjb2xvcjojZmZmfS5vcGVyYXRvcjpob3ZlcntiYWNrZ3JvdW5kOiNmYjhjMDB9LmVxdWFsc3tiYWNrZ3JvdW5kOiMxOTc2ZDI7Y29sb3I6I2ZmZjt3aWR0aDoxMDAlO21hcmdpbi10b3A6NnB4fS5lcXVhbHM6aG92ZXJ7YmFja2dyb3VuZDojMjE5NmYzfTwvc3R5bGU+PC9oZWFkPjxib2R5PjxkaXYgaWQ9ImNhbGN1bGF0b3IiPjxpbnB1dCBpZD0iZGlzcGxheSIgdHlwZT0idGV4dCIgcmVhZG9ubHk9InJlYWRvbmx5Ij48ZGl2PjxidXR0b24gZGF0YS12YWw9IjciPjc8L2J1dHRvbj48YnV0dG9uIGRhdGEtdmFsPSI4Ij44PC9idXR0b24+PGJ1dHRvbiBkYXRhLXZhbD0iOSI+OTwvYnV0dG9uPjxidXR0b24gZGF0YS12YWw9Ii8iIGNsYXNzPSJvcGVyYXRvciI+w7c8L2J1dHRvbj48L2Rpdj48ZGl2PjxidXR0b24gZGF0YS12YWw9IjQiPjQ8L2J1dHRvbj48YnV0dG9uIGRhdGEtdmFsPSI1Ij41PC9idXR0b24+PGJ1dHRvbiBkYXRhLXZhbD0iNiI+NjwvYnV0dG9uPjxidXR0b24gZGF0YS12YWw9IioiIGNsYXNzPSJvcGVyYXRvciI+w5c8L2J1dHRvbj48L2Rpdj48ZGl2PjxidXR0b24gZGF0YS12YWw9IjEiPjE8L2J1dHRvbj48YnV0dG9uIGRhdGEtdmFsPSIyIj4yPC9idXR0b24+PGJ1dHRvbiBkYXRhLXZhbD0iMyI+MzwvYnV0dG9uPjxidXR0b24gZGF0YS12YWw9Ii0iIGNsYXNzPSJvcGVyYXRvciI+4oiSPC9idXR0b24+PC9kaXY+PGRpdj48YnV0dG9uIGRhdGEtdmFsPSIwIj4wPC9idXR0b24+PGJ1dHRvbiBkYXRhLXZhbD0iLiI+LjwvYnV0dG9uPjxidXR0b24gaWQ9ImNsZWFyIj5DPC9idXR0b24+PGJ1dHRvbiBkYXRhLXZhbD0iKyIgY2xhc3M9Im9wZXJhdG9yIj4rPC9idXR0b24+PC9kaXY+PGJ1dHRvbiBpZD0iZXF1YWxzIiBjbGFzcz0iZXF1YWxzIj49PC9idXR0b24+PC9kaXY+PHNjcmlwdD5jb25zdCBkaXNwbGF5ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2Rpc3BsYXknKTsNCiAgY29uc3QgYnV0dG9ucyA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJyNjYWxjdWxhdG9yIGJ1dHRvbltkYXRhLXZhbF0nKTsNCiAgY29uc3QgY2xlYXJCdG4gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnY2xlYXInKTsNCiAgY29uc3QgZXF1YWxzQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2VxdWFscycpOw0KDQogIGJ1dHRvbnMuZm9yRWFjaChidG4gPT4gew0KICAgIGJ0bi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHsNCiAgICAgIGRpc3BsYXkudmFsdWUgKz0gYnRuLmdldEF0dHJpYnV0ZSgnZGF0YS12YWwnKTsNCiAgICB9KTsNCiAgfSk7DQoNCiAgY2xlYXJCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7DQogICAgZGlzcGxheS52YWx1ZSA9ICcnOw0KICB9KTsNCg0KICBlcXVhbHNCdG4uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7DQogICAgdHJ5IHsNCiAgICAgIC8vIEV2YWx1YXRlIGV4cHJlc3Npb24gc2FmZWx5DQogICAgICAvLyBSZXBsYWNlIG11bHRpcGxpY2F0aW9uIGFuZCBkaXZpc2lvbiBzeW1ib2xzIGZpcnN0IChpbiBjYXNlIHlvdSB3YW50IHRvIHN1cHBvcnQgw7cgYW5kIMOXIGluIGRpc3BsYXkpDQogICAgICBjb25zdCBleHByID0gZGlzcGxheS52YWx1ZS5yZXBsYWNlKC/Dty9nLCAnLycpLnJlcGxhY2UoL8OXL2csICcqJyk7DQogICAgICBjb25zdCByZXN1bHQgPSBGdW5jdGlvbignInVzZSBzdHJpY3QiO3JldHVybiAoJyArIGV4cHIgKyAnKScpKCk7DQogICAgICBkaXNwbGF5LnZhbHVlID0gcmVzdWx0Ow0KICAgIH0gY2F0Y2ggew0KICAgICAgZGlzcGxheS52YWx1ZSA9ICdFcnJvcic7DQogICAgfQ0KICB9KTs8L3NjcmlwdD48L2JvZHk+PC9odG1sPg==`, + notepad: "data:text/html;charset=utf-8;base64,PGh0bWw+PGhlYWQ+PHRpdGxlPm5vdGVwYWQ8L3RpdGxlPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Ym9keXttYXJnaW46MH10ZXh0YXJlYXtyZXNpemU6bm9uZTt3aWR0aDo5OCU7aGVpZ2h0Ojk4JTtwb3NpdGlvbjphYnNvbHV0ZTt0b3A6MDtib3JkZXI6bm9uZTttYXJnaW46MSU7Zm9udC1mYW1pbHk6bW9ub3NwYWNlO2ZvbnQtc2l6ZToxZW07b3ZlcmZsb3c6aGlkZGVufXRleHRhcmVhOmZvY3Vze291dGxpbmU6MH08L3N0eWxlPjwvaGVhZD48Ym9keT48dGV4dGFyZWE+PC90ZXh0YXJlYT48L2JvZHk+PC9odG1sPg==" +} + +var shell = { + taskbar_wind: null, + taskbar_size: 25, + startmenuWidth: 400, + startmenuHeight: 300 +}; + +function doTheBullshitByShellBruh() { + const taskbarWind = shell.taskbar_wind; + const taskbarSize = shell.taskbar_size; + const desktopRect = desktop.getBoundingClientRect(); + transformWindow(taskbarWind.windID, + desktopRect.width, + taskbarSize, + 0, + desktopRect.height - taskbarSize + ) +} + +shell.launch = function(app) { + if (app === "Notepad") { + createWindow(embeddedApps.notepad, { + resizable: true, + name: "Notepad" + }); // Launch notepad + } else if (app === "Calculator") { + const calc = createWindow(embeddedApps.calc, { + resizable: true, + name: "Calculator" + }); // Launch ZE CALCULATOR + calc.minSizeX = 500; + calc.minSizeY = 444; + updateWindowMin(calc.windID); + } +}; + +shell.updateTaskbar = doTheBullshitByShellBruh; + +shell.init = function () { + // Create wind for shell using API + const taskbarWind = createWindow("about:blank", { + headerless: true, + resizable: false, + indent: false + }); + + shell.taskbar_wind = taskbarWind; + taskbarWind.minSizeX = 0; + taskbarWind.minSizeY = 0; + taskbarWind.alwaysOnTop = true; + taskbarWind.hideInTaskbar = true; + + // Inject base HTML for taskbar + const tbDoc = taskbarWind.iframe.contentDocument || taskbarWind.iframe.contentWindow.document; + tbDoc.open(); + tbDoc.write(` + + + + + + +
Start
+
+
+ + + + `); + tbDoc.close(); + + shell.updateTaskbar(); + shell.updateTaskbarContents(); + + window.addEventListener('resize', function () { + shell.updateTaskbar(); + }, true); + // Add hook + windowHooks.push(shell.updateTaskbarContents); + + + + // Create start menu window + const startMenu = createWindow("about:blank", { + headerless: true, + resizable: false, + indent: false, + layer: "foreground", + name: "Start Menu" + }); + shell.startMenu = startMenu; + startMenu.hideInTaskbar = true; + shell.updateTaskbarContents(); + + // Inject start menu HTML into its iframe + const smDoc = startMenu.iframe.contentDocument || startMenu.iframe.contentWindow.document; + smDoc.open(); + smDoc.write(` + + + + + + +

Start Menu

+ + + + `); + smDoc.close(); + + // Initially hide the start menu by sizing it to zero and positioning it off-screen + transformWindow(startMenu.windID, 0, 0, 0, window.innerHeight); + + // Helper function to show start menu + function showStartMenu() { + const desktopRect = desktop.getBoundingClientRect(); + const taskbarHeight = shell.taskbar_size || 40; + const width = shell.startmenuWidth; + const height = shell.startmenuHeight; + const x = 0; + const y = desktopRect.height - taskbarHeight - height; + transformWindow(startMenu.windID, width, height, x, y,true); + focusWindow(startMenu.windID); + } + + // Helper function to hide start menu + function hideStartMenu() { + transformWindow(startMenu.windID, 0, 0, 0, window.innerHeight + shell.startmenuHeight,true); + } + + let startMenuVisible = false; + + // Hook start button to toggle start menu + const startBtn = tbDoc.getElementById('startBtn'); + startBtn.addEventListener('click', () => { + if (startMenuVisible) { + hideStartMenu(); + startMenuVisible = false; + } else { + showStartMenu(); + startMenuVisible = true; + } + }); + + // Close start menu when clicking outside (optional) + window.addEventListener('mousedown', (e) => { + if (!startMenuVisible) return; + const smElem = startMenu.windElems.windowDiv; + if (!smElem.contains(e.target)) { + hideStartMenu(); + startMenuVisible = false; + } + }); + + // Optionally, listen for start menu item clicks + window.addEventListener('message', (event) => { + if (event.data?.type === 'startItem' && event.data.action === 'openApp') { + console.log('Open app:', event.data.app); + // Close start menu on selection + hideStartMenu(); + startMenuVisible = false; + shell.launch(event.data.app); + } + }); + + hideStartMenu(); + +}; + +shell.updateTaskbarContents = function () { + const taskbarWind = shell.taskbar_wind; + if (!taskbarWind) return; + + const tbDoc = taskbarWind.iframe.contentDocument || taskbarWind.iframe.contentWindow.document; + const taskList = tbDoc.getElementById('taskList'); + if (!taskList) return; + + // Clear old tasks + taskList.innerHTML = ''; + + // Add a button for each open window except the taskbar + for (const id in windows) { + const winObj = windows[id]; + if (!winObj || id === taskbarWind.windID) continue; + if (winObj.hideInTaskbar) continue; + + const btn = tbDoc.createElement('div'); + btn.className = 'task-item'; + btn.textContent = winObj.windElems.title.textContent || 'Window'; + + btn.addEventListener('click', () => { + // Focus the clicked window + winObj.windElems.windowDiv.style.zIndex = ++zIndexCounter; + }); + + taskList.appendChild(btn); + } +}; diff --git a/style.css b/style.css new file mode 100644 index 0000000..2398e23 --- /dev/null +++ b/style.css @@ -0,0 +1,85 @@ + +body { + margin: 0; + font-family: Arial, sans-serif; + background: #282c34; + height: 100vh; + overflow: hidden; + user-select: none; +} + +#new-window-btn { + position: fixed; + top: 10px; + left: 10px; + padding: 8px 16px; + z-index: 1000; + cursor: pointer; +} + +#desktop { + position: relative; + width: 100vw; + height: 100vh; + overflow: hidden; +} + +.window { + position: absolute; + width: 400px; + height: 300px; + border: 1px solid #888; + background: white; + box-shadow: 0 0 10px rgba(0,0,0,0.7); + display: flex; + flex-direction: column; + user-select: none; +} + +.window-header { + background: #444; + color: white; + padding: 6px 10px; + cursor: grab; + display: flex; + justify-content: space-between; + align-items: center; +} + +.window-header:active { + cursor: grabbing; +} + +.window-title { + font-weight: bold; + font-size: 14px; +} + +.window-close-btn { + background: #ff4d4d; + border: none; + color: white; + font-weight: bold; + cursor: pointer; + width: 22px; + height: 22px; + border-radius: 3px; + display: flex; + align-items: center; + justify-content: center; + line-height: 0; +} + +.window-iframe { + flex: 1; + border: none; + width: 100%; +} + +.window-hidden { + display: none !important; +} + +.background-layer-windower { + z-index: -4; +} \ No newline at end of file