feat: Add initial mess
This commit is contained in:
parent
1988edaafe
commit
59fa42e6c0
BIN
icons/folder.png
Normal file
BIN
icons/folder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
icons/notepad.png
Normal file
BIN
icons/notepad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 843 B |
BIN
icons/trash.png
Normal file
BIN
icons/trash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 671 B |
15
index.html
Normal file
15
index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="stylesheet" href="styles.css" />
|
||||||
|
<title>My Desktop Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="desktop">
|
||||||
|
</div>
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
259
script.js
Normal file
259
script.js
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
let highestZIndex = 100;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const desktop = document.querySelector(".desktop");
|
||||||
|
|
||||||
|
const shortcutData = [
|
||||||
|
{
|
||||||
|
label: "My Files",
|
||||||
|
icon: "icons/folder.png",
|
||||||
|
width: 500,
|
||||||
|
height: 400,
|
||||||
|
action: () =>
|
||||||
|
openNewWindow(
|
||||||
|
"My Files",
|
||||||
|
`<iframe src="https://98.js.org"></iframe>`,
|
||||||
|
true,
|
||||||
|
500,
|
||||||
|
400
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Notes",
|
||||||
|
icon: "icons/notepad.png",
|
||||||
|
width: 400,
|
||||||
|
height: 300,
|
||||||
|
action: () =>
|
||||||
|
openNewWindow(
|
||||||
|
"Notes",
|
||||||
|
`<textarea>hello...</textarea>`,
|
||||||
|
true,
|
||||||
|
400,
|
||||||
|
300
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Recycle Bin",
|
||||||
|
icon: "icons/trash.png",
|
||||||
|
width: 300,
|
||||||
|
height: 200,
|
||||||
|
action: () =>
|
||||||
|
openNewWindow(
|
||||||
|
"Recycle Bin",
|
||||||
|
`<p>Your trash goes here.</p>`,
|
||||||
|
false,
|
||||||
|
300,
|
||||||
|
200
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "CSS Art",
|
||||||
|
icon: "icons/folder.png",
|
||||||
|
width: 600,
|
||||||
|
height: 500,
|
||||||
|
action: () =>
|
||||||
|
openNewWindow(
|
||||||
|
"CSS Art",
|
||||||
|
`<iframe src="https://tilde.town/~dozens/cssart/quarters.html"></iframe>`,
|
||||||
|
true,
|
||||||
|
600,
|
||||||
|
600
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
shortcutData.forEach(({ label, icon, action }) => {
|
||||||
|
const shortcut = document.createElement("div");
|
||||||
|
shortcut.className = "shortcut";
|
||||||
|
|
||||||
|
shortcut.innerHTML = `
|
||||||
|
<img src="${icon}" alt="${label}" />
|
||||||
|
<span>${label}</span>
|
||||||
|
`;
|
||||||
|
|
||||||
|
shortcut.addEventListener("dblclick", action);
|
||||||
|
document.querySelector(".desktop").appendChild(shortcut);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
function openNewWindow(title, contentHTML, fullSizeContent = false, width = 300, height = 200) {
|
||||||
|
const newWin = document.createElement("div");
|
||||||
|
newWin.className = "window";
|
||||||
|
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
const viewportHeight = window.innerHeight;
|
||||||
|
const randomLeft = Math.floor(Math.random() * (viewportWidth - 320));
|
||||||
|
const randomTop = Math.floor(Math.random() * (viewportHeight - 200));
|
||||||
|
newWin.style.left = `${randomLeft}px`;
|
||||||
|
newWin.style.top = `${randomTop}px`;
|
||||||
|
|
||||||
|
newWin.innerHTML = `
|
||||||
|
<div class="titlebar">
|
||||||
|
<div class="title">${title}</div>
|
||||||
|
<button class="close-btn" aria-label="Close window">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="content">${contentHTML}</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const resizers = ['nw', 'n', 'ne', 'e', 'se', 's', 'sw', 'w'];
|
||||||
|
resizers.forEach(pos => {
|
||||||
|
const resizer = document.createElement("div");
|
||||||
|
resizer.className = `resizer ${pos}`;
|
||||||
|
newWin.appendChild(resizer);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.appendChild(newWin);
|
||||||
|
|
||||||
|
bringToFront(newWin);
|
||||||
|
|
||||||
|
const content = newWin.querySelector(".content");
|
||||||
|
content.style.width = `${width}px`;
|
||||||
|
content.style.height = `${height}px`;
|
||||||
|
|
||||||
|
if (fullSizeContent) {
|
||||||
|
content.style.padding = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring window to front when clicked
|
||||||
|
newWin.addEventListener("mousedown", () => {
|
||||||
|
bringToFront(newWin);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dragging logic
|
||||||
|
const titlebar = newWin.querySelector(".titlebar");
|
||||||
|
let isDragging = false;
|
||||||
|
let offsetX = 0;
|
||||||
|
let offsetY = 0;
|
||||||
|
|
||||||
|
titlebar.addEventListener("mousedown", (e) => {
|
||||||
|
if (e.target.classList.contains("close-btn")) return;
|
||||||
|
isDragging = true;
|
||||||
|
const rect = newWin.getBoundingClientRect();
|
||||||
|
offsetX = e.clientX - rect.left;
|
||||||
|
offsetY = e.clientY - rect.top;
|
||||||
|
document.body.style.userSelect = 'none';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", (e) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
newWin.style.left = `${e.clientX - offsetX}px`;
|
||||||
|
newWin.style.top = `${e.clientY - offsetY}px`;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("mouseup", () => {
|
||||||
|
if (isDragging) {
|
||||||
|
isDragging = false;
|
||||||
|
document.body.style.userSelect = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close button logic
|
||||||
|
const closeBtn = newWin.querySelector(".close-btn");
|
||||||
|
closeBtn.addEventListener("click", () => {
|
||||||
|
newWin.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
let isResizing = false;
|
||||||
|
let currentResizer = null;
|
||||||
|
|
||||||
|
const minWidth = 200;
|
||||||
|
const minHeight = 150;
|
||||||
|
|
||||||
|
const startResize = (e, resizer) => {
|
||||||
|
e.preventDefault();
|
||||||
|
isResizing = true;
|
||||||
|
currentResizer = resizer;
|
||||||
|
|
||||||
|
const rect = newWin.getBoundingClientRect();
|
||||||
|
newWin.dataset.startX = e.clientX;
|
||||||
|
newWin.dataset.startY = e.clientY;
|
||||||
|
newWin.dataset.startWidth = rect.width;
|
||||||
|
newWin.dataset.startHeight = rect.height;
|
||||||
|
newWin.dataset.startLeft = rect.left;
|
||||||
|
newWin.dataset.startTop = rect.top;
|
||||||
|
|
||||||
|
document.body.style.userSelect = "none";
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMouseMove = (e) => {
|
||||||
|
if (!isResizing || !currentResizer) return;
|
||||||
|
|
||||||
|
const dx = e.clientX - newWin.dataset.startX;
|
||||||
|
const dy = e.clientY - newWin.dataset.startY;
|
||||||
|
|
||||||
|
let newWidth = parseFloat(newWin.dataset.startWidth);
|
||||||
|
let newHeight = parseFloat(newWin.dataset.startHeight);
|
||||||
|
let newLeft = parseFloat(newWin.dataset.startLeft);
|
||||||
|
let newTop = parseFloat(newWin.dataset.startTop);
|
||||||
|
|
||||||
|
switch (currentResizer) {
|
||||||
|
case 'e':
|
||||||
|
newWidth += dx;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
newHeight += dy;
|
||||||
|
break;
|
||||||
|
case 'se':
|
||||||
|
newWidth += dx;
|
||||||
|
newHeight += dy;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
newWidth -= dx;
|
||||||
|
newLeft += dx;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
newHeight -= dy;
|
||||||
|
newTop += dy;
|
||||||
|
break;
|
||||||
|
case 'nw':
|
||||||
|
newWidth -= dx;
|
||||||
|
newLeft += dx;
|
||||||
|
newHeight -= dy;
|
||||||
|
newTop += dy;
|
||||||
|
break;
|
||||||
|
case 'ne':
|
||||||
|
newWidth += dx;
|
||||||
|
newHeight -= dy;
|
||||||
|
newTop += dy;
|
||||||
|
break;
|
||||||
|
case 'sw':
|
||||||
|
newWidth -= dx;
|
||||||
|
newLeft += dx;
|
||||||
|
newHeight += dy;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newWidth >= minWidth) {
|
||||||
|
newWin.style.width = `${newWidth}px`;
|
||||||
|
newWin.style.left = `${newLeft}px`;
|
||||||
|
content.style.width = `${newWidth}px`;
|
||||||
|
}
|
||||||
|
if (newHeight >= minHeight) {
|
||||||
|
newWin.style.height = `${newHeight}px`;
|
||||||
|
newWin.style.top = `${newTop}px`;
|
||||||
|
content.style.height = `${newHeight - titlebar.offsetHeight}px`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopResize = () => {
|
||||||
|
isResizing = false;
|
||||||
|
currentResizer = null;
|
||||||
|
document.body.style.userSelect = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
resizers.forEach(pos => {
|
||||||
|
const el = newWin.querySelector(`.resizer.${pos}`);
|
||||||
|
el.addEventListener("mousedown", (e) => startResize(e, pos));
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", onMouseMove);
|
||||||
|
document.addEventListener("mouseup", stopResize);
|
||||||
|
|
||||||
|
function bringToFront(win) {
|
||||||
|
highestZIndex++;
|
||||||
|
win.style.zIndex = highestZIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
156
styles.css
Normal file
156
styles.css
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
body {
|
||||||
|
font-family: Tahoma, sans-serif;
|
||||||
|
margin: 2rem;
|
||||||
|
background-color: #e0b0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window {
|
||||||
|
position: absolute;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow:
|
||||||
|
0 4px 8px rgba(0, 0, 0, 0.3), /* Soft outer shadow */
|
||||||
|
inset 0 0 5px rgba(255, 255, 255, 0.1); /* Inner highlight for 3D */
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
cursor: default;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: auto; /* allow flexible height */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titlebar .title {
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titlebar {
|
||||||
|
width: 100%;
|
||||||
|
height: 30px;
|
||||||
|
background: linear-gradient(to bottom, #8a2be2, #6a1dbf);
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: move;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
box-shadow: inset 0 -1px 1px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 10px;
|
||||||
|
background: #C0C0C0;
|
||||||
|
overflow: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn:hover {
|
||||||
|
color: #ffaaaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, 80px);
|
||||||
|
grid-auto-rows: 100px;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut {
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
color: black;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut span {
|
||||||
|
display: block;
|
||||||
|
text-shadow: 1px 1px 2px white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content textarea {
|
||||||
|
font-family: monospace;
|
||||||
|
padding: 8px;
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content iframe,
|
||||||
|
.content textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window .resizer {
|
||||||
|
position: absolute;
|
||||||
|
background: transparent;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Corners */
|
||||||
|
.window .resizer.nw, .window .resizer.ne,
|
||||||
|
.window .resizer.sw, .window .resizer.se {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window .resizer.nw { top: 0; left: 0; cursor: nw-resize; }
|
||||||
|
.window .resizer.ne { top: 0; right: 0; cursor: ne-resize; }
|
||||||
|
.window .resizer.sw { bottom: 0; left: 0; cursor: sw-resize; }
|
||||||
|
.window .resizer.se { bottom: 0; right: 0; cursor: se-resize; }
|
||||||
|
|
||||||
|
/* Sides */
|
||||||
|
.window .resizer.n, .window .resizer.s {
|
||||||
|
height: 6px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window .resizer.n { top: 0; }
|
||||||
|
.window .resizer.s { bottom: 0; }
|
||||||
|
|
||||||
|
.window .resizer.e, .window .resizer.w {
|
||||||
|
width: 6px;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window .resizer.e { right: 0; }
|
||||||
|
.window .resizer.w { left: 0; }
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user