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