mirror of
https://github.com/p0yoy01dev/njz_is_five
synced 2026-06-02 23:15:27 +02:00
273 lines
13 KiB
HTML
273 lines
13 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<title>Windows 7 Explorer Demo</title>
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/khang-nd/7.css/7.css">
|
||
<style>
|
||
:root{--taskbar-height:56px;--text:#eaf2ff;--muted:#bdd3f2;--accent:#2b579a;--bg1:#071827;--bg2:#0b2b49;}
|
||
*{box-sizing:border-box}
|
||
html,body{height:100%;margin:0;font-family:"Segoe UI",Arial,sans-serif;background:linear-gradient(135deg,var(--bg1),var(--bg2));color:var(--text)}
|
||
.wallpaper{position:absolute;inset:0;pointer-events:none;display:flex;align-items:center;justify-content:center}
|
||
.logo{width:620px;height:300px;opacity:.07;filter:blur(18px);background:linear-gradient(90deg,#00a2ff,#2baf2b,#ffb400,#ff3b30);border-radius:16px;transform:rotate(-8deg)}
|
||
.win7-window{position:absolute;top:70px;left:70px;width:min(1040px,calc(100% - 140px));height:min(640px,calc(100% - 160px));display:flex;flex-direction:column;background:rgba(4,10,20,.65);border:1px solid rgba(255,255,255,.1);border-radius:10px;box-shadow:0 20px 56px rgba(0,0,0,.6);overflow:hidden}
|
||
.titlebar{display:flex;align-items:center;gap:10px;padding:10px 12px;border-bottom:1px solid rgba(255,255,255,.08);background:linear-gradient(180deg,rgba(255,255,255,.08),rgba(255,255,255,.02))}
|
||
.title{font-weight:700}
|
||
.controls{margin-left:auto;display:flex;gap:8px}.btn{width:44px;height:30px;border:0;border-radius:6px;cursor:pointer;font-weight:700}
|
||
.btn.min{background:#dcecff}.btn.max{background:#2d8ad9;color:#fff}.btn.close{background:#d44f4f;color:#fff}
|
||
.toolbar{display:flex;flex-wrap:wrap;gap:8px;padding:10px 12px;border-bottom:1px solid rgba(255,255,255,.08)}
|
||
.toolbar button{border:1px solid rgba(255,255,255,.24);background:rgba(255,255,255,.08);color:var(--text);border-radius:6px;padding:6px 10px;cursor:pointer}
|
||
.pathbar{display:flex;align-items:center;gap:4px;padding:8px 12px;font-size:13px;background:rgba(0,0,0,.18);border-bottom:1px solid rgba(255,255,255,.08)}
|
||
.crumb{color:#d8ebff;cursor:pointer}.crumb:hover{text-decoration:underline}
|
||
.content{display:grid;grid-template-columns:230px 1fr;gap:12px;padding:12px;height:100%}
|
||
.sidebar,.main{border-radius:8px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1)}
|
||
.sidebar{padding:12px}.sidebar h4{margin:0 0 10px 0}.tree-node{padding:7px;border-radius:6px;cursor:pointer}.tree-node:hover{background:rgba(255,255,255,.08)}
|
||
.main{display:flex;flex-direction:column;overflow:hidden}
|
||
.list{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px;padding:12px;overflow:auto}
|
||
.item{padding:10px;border-radius:8px;background:rgba(6,16,30,.6);border:1px solid rgba(255,255,255,.12);cursor:pointer;user-select:none}
|
||
.item.selected{outline:2px solid #8ec5ff}.item-name{font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||
.item-meta{font-size:12px;color:var(--muted);margin-top:5px}
|
||
.status{padding:8px 12px;font-size:12px;color:var(--muted);border-top:1px solid rgba(255,255,255,.08)}
|
||
.taskbar{position:fixed;left:8px;right:8px;bottom:8px;height:var(--taskbar-height);display:flex;align-items:center;justify-content:space-between;padding:8px 10px;border-radius:12px;background:linear-gradient(180deg,rgba(6,13,25,.65),rgba(3,7,14,.85));box-shadow:0 12px 40px rgba(0,0,0,.6)}
|
||
.taskbar-left{display:flex;align-items:center;gap:10px}
|
||
.start{display:flex;align-items:center;gap:8px}.orb{width:34px;height:34px;border-radius:8px;background:linear-gradient(135deg,#2fb3ff,#ffd05a)}
|
||
.taskbar-app{border:1px solid rgba(255,255,255,.32);background:rgba(255,255,255,.12);color:var(--text);padding:8px 12px;border-radius:8px;cursor:pointer}
|
||
.taskbar-app.active{background:rgba(142,197,255,.35)}
|
||
@media (max-width:900px){.content{grid-template-columns:1fr}.sidebar{display:none}.win7-window{left:16px;top:20px;width:calc(100% - 32px);height:calc(100% - 90px)}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wallpaper"><div class="logo"></div></div>
|
||
|
||
<section class="win7-window" aria-label="Windows Explorer">
|
||
<div class="titlebar">
|
||
<div style="width:28px;height:28px;border-radius:6px;background:var(--accent);display:grid;place-items:center;">📁</div>
|
||
<div class="title">Windows Explorer — Music Library</div>
|
||
<div class="controls">
|
||
<button class="btn min" id="min-btn">−</button>
|
||
<button class="btn max" id="max-btn">▢</button>
|
||
<button class="btn close" id="close-btn">✕</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toolbar">
|
||
<button id="up-btn">Up</button>
|
||
<button id="new-folder-btn">New Folder</button>
|
||
<button id="new-file-btn">New Text File</button>
|
||
<button id="rename-btn">Rename</button>
|
||
<button id="delete-btn">Delete</button>
|
||
</div>
|
||
|
||
<div class="pathbar" id="pathbar"></div>
|
||
|
||
<div class="content">
|
||
<aside class="sidebar">
|
||
<h4>Libraries</h4>
|
||
<div class="tree-node" data-path="/">🖥️ Computer</div>
|
||
<div class="tree-node" data-path="/Music">🎵 Music</div>
|
||
<div class="tree-node" data-path="/Music/NewJeans">💿 NewJeans</div>
|
||
<div class="tree-node" data-path="/Music/Blackpink">💿 Blackpink</div>
|
||
</aside>
|
||
<main class="main">
|
||
<div class="list" id="file-list"></div>
|
||
<div class="status" id="status">Ready</div>
|
||
</main>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="taskbar">
|
||
<div class="taskbar-left">
|
||
<div class="start"><div class="orb"></div><strong>Start</strong></div>
|
||
<button class="taskbar-app active" id="explorer-taskbar-btn">📁 Windows Explorer</button>
|
||
</div>
|
||
<div id="clock"></div>
|
||
</div>
|
||
|
||
<script>
|
||
const fs = {
|
||
type: 'folder',
|
||
children: {
|
||
Music: {
|
||
type: 'folder',
|
||
children: {
|
||
NewJeans: {
|
||
type: 'folder',
|
||
children: {
|
||
'Attention.mp3': { type: 'file', size: '7.0 MB' },
|
||
'Hype Boy.mp3': { type: 'file', size: '6.8 MB' },
|
||
'Ditto.mp3': { type: 'file', size: '7.2 MB' },
|
||
'OMG.mp3': { type: 'file', size: '6.5 MB' },
|
||
'Super Shy.mp3': { type: 'file', size: '6.2 MB' }
|
||
}
|
||
},
|
||
Blackpink: {
|
||
type: 'folder',
|
||
children: {
|
||
'DDU-DU DDU-DU.mp3': { type: 'file', size: '7.3 MB' },
|
||
'How You Like That.mp3': { type: 'file', size: '7.1 MB' },
|
||
'Kill This Love.mp3': { type: 'file', size: '7.0 MB' },
|
||
'Pink Venom.mp3': { type: 'file', size: '6.9 MB' },
|
||
'Shut Down.mp3': { type: 'file', size: '6.7 MB' }
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
const fileList = document.getElementById('file-list');
|
||
const pathBar = document.getElementById('pathbar');
|
||
const status = document.getElementById('status');
|
||
let currentPath = ['/'];
|
||
let selectedName = null;
|
||
|
||
const normalize = (path) => path === '/' ? ['/'] : ['/', ...path.split('/').filter(Boolean)];
|
||
|
||
function getNode(pathArr) {
|
||
let node = fs;
|
||
for (const part of pathArr.slice(1)) {
|
||
if (!node.children || !node.children[part]) return null;
|
||
node = node.children[part];
|
||
}
|
||
return node;
|
||
}
|
||
|
||
function openPath(path) {
|
||
const target = normalize(path);
|
||
if (getNode(target)?.type !== 'folder') return;
|
||
currentPath = target;
|
||
selectedName = null;
|
||
render();
|
||
}
|
||
|
||
function renderPath() {
|
||
pathBar.innerHTML = '';
|
||
let built = '';
|
||
currentPath.forEach((part, i) => {
|
||
built = i === 0 ? '/' : `${built === '/' ? '' : built}/${part}`;
|
||
const crumb = document.createElement('span');
|
||
crumb.className = 'crumb';
|
||
crumb.textContent = i === 0 ? 'Computer' : part;
|
||
crumb.onclick = () => openPath(built);
|
||
pathBar.appendChild(crumb);
|
||
if (i < currentPath.length - 1) pathBar.append(' › ');
|
||
});
|
||
}
|
||
|
||
function renderList() {
|
||
const folder = getNode(currentPath);
|
||
const entries = Object.entries(folder.children || {}).sort((a, b) => a[0].localeCompare(b[0]));
|
||
fileList.innerHTML = '';
|
||
|
||
for (const [name, node] of entries) {
|
||
const el = document.createElement('article');
|
||
el.className = `item${selectedName === name ? ' selected' : ''}`;
|
||
el.innerHTML = `<div class="item-name">${node.type === 'folder' ? '📁' : '🎵'} ${name}</div>
|
||
<div class="item-meta">${node.type === 'folder' ? 'File Folder' : node.size}</div>`;
|
||
el.onclick = () => {
|
||
selectedName = name;
|
||
renderList();
|
||
status.textContent = `${name} selected`;
|
||
};
|
||
el.ondblclick = () => {
|
||
if (node.type === 'folder') openPath(`${currentPath.join('/').replace('//', '/')}/${name}`);
|
||
else status.textContent = `Now playing: ${name}`;
|
||
};
|
||
fileList.appendChild(el);
|
||
}
|
||
|
||
status.textContent = `${entries.length} item(s) in ${currentPath[currentPath.length - 1] === '/' ? 'Computer' : currentPath[currentPath.length - 1]}`;
|
||
}
|
||
|
||
function render() {
|
||
renderPath();
|
||
renderList();
|
||
}
|
||
|
||
function parentPath() {
|
||
if (currentPath.length === 1) return ['/'];
|
||
return currentPath.slice(0, -1);
|
||
}
|
||
|
||
document.getElementById('up-btn').onclick = () => { currentPath = parentPath(); selectedName = null; render(); };
|
||
document.getElementById('new-folder-btn').onclick = () => {
|
||
const name = prompt('Folder name?');
|
||
if (!name) return;
|
||
const node = getNode(currentPath);
|
||
if (node.children[name]) return alert('Name already exists.');
|
||
node.children[name] = { type: 'folder', children: {} };
|
||
render();
|
||
};
|
||
document.getElementById('new-file-btn').onclick = () => {
|
||
const name = prompt('Text file name?', 'New File.txt');
|
||
if (!name) return;
|
||
const node = getNode(currentPath);
|
||
if (node.children[name]) return alert('Name already exists.');
|
||
node.children[name] = { type: 'file', size: '1 KB' };
|
||
render();
|
||
};
|
||
document.getElementById('rename-btn').onclick = () => {
|
||
if (!selectedName) return alert('Select a file or folder first.');
|
||
const node = getNode(currentPath);
|
||
const next = prompt('Rename to?', selectedName);
|
||
if (!next || next === selectedName) return;
|
||
if (node.children[next]) return alert('Name already exists.');
|
||
node.children[next] = node.children[selectedName];
|
||
delete node.children[selectedName];
|
||
selectedName = next;
|
||
render();
|
||
};
|
||
document.getElementById('delete-btn').onclick = () => {
|
||
if (!selectedName) return alert('Select a file or folder first.');
|
||
const node = getNode(currentPath);
|
||
delete node.children[selectedName];
|
||
selectedName = null;
|
||
render();
|
||
};
|
||
|
||
const windowEl = document.querySelector('.win7-window');
|
||
const explorerTaskbarBtn = document.getElementById('explorer-taskbar-btn');
|
||
|
||
function showExplorerWindow() {
|
||
if (!document.body.contains(windowEl)) return;
|
||
windowEl.style.display = 'flex';
|
||
explorerTaskbarBtn.classList.add('active');
|
||
status.textContent = 'Explorer restored';
|
||
}
|
||
|
||
function minimizeExplorerWindow() {
|
||
if (!document.body.contains(windowEl)) return;
|
||
windowEl.style.display = 'none';
|
||
explorerTaskbarBtn.classList.remove('active');
|
||
}
|
||
|
||
document.querySelectorAll('.tree-node').forEach((item) => item.onclick = () => openPath(item.dataset.path));
|
||
document.getElementById('close-btn').onclick = () => {
|
||
windowEl.remove();
|
||
explorerTaskbarBtn.disabled = true;
|
||
explorerTaskbarBtn.classList.remove('active');
|
||
explorerTaskbarBtn.textContent = '📁 Windows Explorer (Closed)';
|
||
};
|
||
document.getElementById('min-btn').onclick = () => minimizeExplorerWindow();
|
||
explorerTaskbarBtn.onclick = () => {
|
||
if (windowEl.style.display === 'none') showExplorerWindow();
|
||
else minimizeExplorerWindow();
|
||
};
|
||
document.getElementById('max-btn').onclick = () => {
|
||
const w = document.querySelector('.win7-window');
|
||
if (!w.dataset.maxed) {
|
||
w.dataset.maxed = '1';
|
||
w.style.top = '8px'; w.style.left = '8px'; w.style.width = 'calc(100% - 16px)'; w.style.height = 'calc(100% - 80px)';
|
||
} else {
|
||
delete w.dataset.maxed;
|
||
w.style.top = '70px'; w.style.left = '70px'; w.style.width = 'min(1040px,calc(100% - 140px))'; w.style.height = 'min(640px,calc(100% - 160px))';
|
||
}
|
||
};
|
||
|
||
const clock = document.getElementById('clock');
|
||
setInterval(() => clock.textContent = new Date().toLocaleString(), 500);
|
||
render();
|
||
</script>
|
||
</body>
|
||
</html>
|