first commit

This commit is contained in:
usernames122
2025-08-09 05:25:35 +02:00
commit 55e1616e1b
7 changed files with 746 additions and 0 deletions

BIN
clickin.wav Normal file

Binary file not shown.

BIN
clickout.wav Normal file

Binary file not shown.

49
immersion.js Normal file
View File

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

23
index.html Normal file
View File

@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LAMINAX Windower</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<noscript>The Laminax OS project needs javascript. Yes even the original terminal version needs it.</noscript>
<div id="desktop"></div>
<div id="background-layer" class="background-layer-windower"></div>
<script src="script.js"></script>
<!-- Load the shell -->
<script src="shell.js"></script>
<script>
window.addEventListener("DOMContentLoaded",shell.init);
</script>
<!-- Load OPTIONAL stuff -->
<script src="immersion.js"></script>
</body>
</html>

303
script.js Normal file
View File

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

286
shell.js Normal file
View File

@@ -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(`
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
background: #333;
color: white;
font-family: sans-serif;
height: 100%;
display: flex;
align-items: center;
overflow: hidden;
}
.start-btn {
padding: 4px 8px;
background: #555;
cursor: pointer;
margin: 0 4px;
border-radius: 3px;
user-select: none;
}
.start-btn:hover {
background: #777;
}
.task-list {
flex: 1;
display: flex;
gap: 4px;
overflow-x: auto;
}
.task-item {
padding: 4px 8px;
background: #444;
cursor: pointer;
border-radius: 3px;
white-space: nowrap;
}
.task-item:hover {
background: #666;
}
.clock {
padding: 0 8px;
}
</style>
</head>
<body>
<div class="start-btn" id="startBtn">Start</div>
<div class="task-list" id="taskList"></div>
<div class="clock" id="clock"></div>
<script>
function updateClock() {
const now = new Date();
document.getElementById('clock').textContent =
now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
setInterval(updateClock, 1000);
updateClock();
</script>
</body>
</html>
`);
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(`
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
background: #222;
color: white;
font-family: sans-serif;
height: 100%;
overflow: auto;
padding: 10px;
box-sizing: border-box;
}
h2 {
margin-top: 0;
font-size: 1.2em;
border-bottom: 1px solid #555;
padding-bottom: 6px;
}
ul {
list-style: none;
padding: 0;
}
li {
padding: 6px 10px;
cursor: pointer;
border-radius: 4px;
}
li:hover {
background: #444;
}
</style>
</head>
<body>
<h2>Start Menu</h2>
<ul id="startItems">
<li onclick="parent.postMessage({type:'startItem', action:'openApp', app:'Calculator'}, '*')">Calculator</li>
<li onclick="parent.postMessage({type:'startItem', action:'openApp', app:'Notepad'}, '*')">Notepad</li>
<li onclick="parent.postMessage({type:'startItem', action:'openApp', app:'Settings'}, '*')">Settings</li>
</ul>
</body>
</html>
`);
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);
}
};

85
style.css Normal file
View File

@@ -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;
}