Created
January 9, 2026 00:21
-
-
Save Shadhujan/91fed5757e310bda0ac3fc83dc34b7b2 to your computer and use it in GitHub Desktop.
Interactive Windows 95 Simulation (Single File HTML/CSS/JS) - Demo for 'Gist Host' Hack
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Windows 95</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| /* --- RESET & FONTS --- */ | |
| * { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| overflow: hidden; | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* Fallback for MS Sans Serif */ | |
| background-color: #008080; /* Classic Teal */ | |
| user-select: none; | |
| height: 100vh; | |
| width: 100vw; | |
| cursor: default; | |
| } | |
| /* --- UTILITIES for the 3D Look --- */ | |
| .win95-border { | |
| background-color: #c0c0c0; | |
| border-top: 2px solid #dfdfdf; | |
| border-left: 2px solid #dfdfdf; | |
| border-right: 2px solid #000000; | |
| border-bottom: 2px solid #000000; | |
| box-shadow: 1px 1px 0px #ffffff inset, -1px -1px 0px #808080 inset; | |
| } | |
| .win95-inset { | |
| background-color: #fff; | |
| border-top: 2px solid #808080; | |
| border-left: 2px solid #808080; | |
| border-right: 2px solid #fff; | |
| border-bottom: 2px solid #fff; | |
| box-shadow: 1px 1px 0px #000 inset; | |
| } | |
| .win95-btn { | |
| font-family: Tahoma, sans-serif; | |
| font-weight: bold; | |
| font-size: 12px; | |
| color: black; | |
| background: #c0c0c0; | |
| border-top: 1px solid #fff; | |
| border-left: 1px solid #fff; | |
| border-right: 1px solid #000; | |
| border-bottom: 1px solid #000; | |
| box-shadow: 1px 1px 0 #808080; | |
| padding: 2px 6px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .win95-btn:active, .win95-btn.active { | |
| border-top: 1px solid #000; | |
| border-left: 1px solid #000; | |
| border-right: 1px solid #fff; | |
| border-bottom: 1px solid #fff; | |
| box-shadow: 1px 1px 0 #808080 inset; | |
| transform: translate(1px, 1px); | |
| } | |
| /* --- DESKTOP ICONS --- */ | |
| #desktop { | |
| width: 100%; | |
| height: calc(100% - 30px); /* Minus taskbar */ | |
| position: relative; | |
| padding: 10px; | |
| display: flex; | |
| flex-direction: column; | |
| flex-wrap: wrap; | |
| align-content: flex-start; | |
| gap: 20px; | |
| } | |
| .desktop-icon { | |
| width: 70px; | |
| text-align: center; | |
| color: white; | |
| cursor: pointer; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| } | |
| .desktop-icon i { | |
| font-size: 32px; | |
| margin-bottom: 5px; | |
| text-shadow: 1px 1px 0 #000; | |
| } | |
| .desktop-icon span { | |
| font-size: 12px; | |
| background: transparent; | |
| padding: 2px; | |
| } | |
| .desktop-icon:active span, .desktop-icon.selected span { | |
| background-color: #000080; | |
| border: 1px dotted #fff; | |
| } | |
| /* --- START MENU --- */ | |
| #start-menu { | |
| position: absolute; | |
| bottom: 32px; | |
| left: 2px; | |
| width: 200px; | |
| background: #c0c0c0; | |
| display: none; | |
| z-index: 9999; | |
| padding: 2px; | |
| } | |
| .sidebar-strip { | |
| width: 25px; | |
| background: #000080; | |
| position: absolute; | |
| left: 2px; | |
| top: 2px; | |
| bottom: 2px; | |
| display: flex; | |
| align-items: flex-end; | |
| } | |
| .sidebar-text { | |
| transform: rotate(-90deg); | |
| color: #c0c0c0; | |
| font-size: 20px; | |
| font-weight: bold; | |
| white-space: nowrap; | |
| margin-bottom: 10px; | |
| margin-left: -5px; | |
| } | |
| .menu-items { | |
| margin-left: 28px; | |
| } | |
| .menu-item { | |
| padding: 8px 10px; | |
| display: flex; | |
| align-items: center; | |
| font-size: 12px; | |
| cursor: pointer; | |
| color: #000; | |
| } | |
| .menu-item:hover { | |
| background-color: #000080; | |
| color: white; | |
| } | |
| .menu-item i { | |
| margin-right: 10px; | |
| width: 20px; | |
| text-align: center; | |
| } | |
| .menu-divider { | |
| height: 1px; | |
| background: #808080; | |
| border-bottom: 1px solid #fff; | |
| margin: 4px 0; | |
| } | |
| /* --- TASKBAR --- */ | |
| #taskbar { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 30px; | |
| background: #c0c0c0; | |
| border-top: 2px solid #fff; | |
| display: flex; | |
| align-items: center; | |
| padding: 2px; | |
| z-index: 10000; | |
| } | |
| #start-btn { | |
| height: 22px; | |
| padding: 0 8px; | |
| display: flex; | |
| align-items: center; | |
| gap: 5px; | |
| margin-right: 5px; | |
| font-weight: bold; | |
| font-style: italic; | |
| } | |
| #task-list { | |
| flex-grow: 1; | |
| display: flex; | |
| gap: 4px; | |
| padding-left: 4px; | |
| overflow: hidden; | |
| } | |
| .task-item { | |
| width: 160px; | |
| height: 22px; | |
| display: flex; | |
| align-items: center; | |
| padding: 0 5px; | |
| font-size: 11px; | |
| text-align: left; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .task-item img, .task-item i { | |
| margin-right: 5px; | |
| } | |
| #tray { | |
| width: 70px; | |
| height: 22px; | |
| background: #c0c0c0; | |
| border-top: 1px solid #808080; | |
| border-left: 1px solid #808080; | |
| border-right: 1px solid #fff; | |
| border-bottom: 1px solid #fff; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 11px; | |
| margin-left: 5px; | |
| } | |
| /* --- WINDOWS --- */ | |
| .window { | |
| position: absolute; | |
| background: #c0c0c0; | |
| padding: 3px; | |
| display: flex; | |
| flex-direction: column; | |
| min-width: 200px; | |
| min-height: 100px; | |
| box-shadow: 2px 2px 10px rgba(0,0,0,0.4); | |
| } | |
| .title-bar { | |
| background: linear-gradient(90deg, #000080, #1084d0); | |
| color: white; | |
| padding: 2px 4px; | |
| font-weight: bold; | |
| font-size: 12px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| cursor: default; | |
| } | |
| .window.inactive .title-bar { | |
| background: #808080; | |
| color: #c0c0c0; | |
| } | |
| .title-controls { | |
| display: flex; | |
| gap: 2px; | |
| } | |
| .control-btn { | |
| width: 16px; | |
| height: 14px; | |
| font-size: 10px; | |
| line-height: 10px; | |
| text-align: center; | |
| padding: 0; | |
| background: #c0c0c0; | |
| } | |
| .window-menu { | |
| display: flex; | |
| padding: 2px 0; | |
| font-size: 12px; | |
| } | |
| .window-menu span { | |
| padding: 0 6px; | |
| cursor: pointer; | |
| } | |
| .window-menu span:hover { | |
| background: #000080; | |
| color: white; | |
| } | |
| .window-menu span u { | |
| text-decoration: underline; | |
| } | |
| .window-content { | |
| flex-grow: 1; | |
| margin-top: 2px; | |
| overflow: auto; | |
| font-family: 'Courier New', Courier, monospace; | |
| font-size: 14px; | |
| } | |
| /* Specific App Styles */ | |
| .notepad-area { | |
| width: 100%; | |
| height: 100%; | |
| resize: none; | |
| border: none; | |
| outline: none; | |
| font-family: 'Fixedsys', 'Courier New', monospace; | |
| padding: 5px; | |
| } | |
| /* Minesweeper */ | |
| .minesweeper-header { | |
| background: #c0c0c0; | |
| padding: 5px; | |
| display: flex; | |
| justify-content: space-between; | |
| border: 2px solid #808080; | |
| border-right-color: #fff; | |
| border-bottom-color: #fff; | |
| margin-bottom: 5px; | |
| } | |
| .mine-lcd { | |
| background: black; | |
| color: red; | |
| font-family: 'Courier New', monospace; | |
| font-weight: bold; | |
| font-size: 18px; | |
| padding: 0 4px; | |
| } | |
| .mine-face { | |
| width: 24px; | |
| height: 24px; | |
| font-size: 16px; | |
| padding: 0; | |
| } | |
| .mine-grid { | |
| display: grid; | |
| grid-template-columns: repeat(9, 20px); | |
| grid-template-rows: repeat(9, 20px); | |
| border-top: 3px solid #808080; | |
| border-left: 3px solid #808080; | |
| border-right: 3px solid #fff; | |
| border-bottom: 3px solid #fff; | |
| } | |
| .mine-cell { | |
| width: 20px; | |
| height: 20px; | |
| background: #c0c0c0; | |
| border-top: 2px solid #fff; | |
| border-left: 2px solid #fff; | |
| border-right: 2px solid #808080; | |
| border-bottom: 2px solid #808080; | |
| font-size: 12px; | |
| font-weight: bold; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: default; | |
| } | |
| .mine-cell.revealed { | |
| border: 1px solid #808080; | |
| background: #c0c0c0; | |
| } | |
| .mine-cell.bomb { | |
| background: red; | |
| } | |
| /* Explorer */ | |
| .explorer-body { | |
| background: white; | |
| padding: 10px; | |
| display: grid; | |
| grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); | |
| gap: 10px; | |
| } | |
| .explorer-item { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| text-align: center; | |
| font-size: 11px; | |
| cursor: pointer; | |
| color: black; | |
| } | |
| .explorer-item i { | |
| font-size: 24px; | |
| margin-bottom: 4px; | |
| color: #000080; /* Folder color */ | |
| } | |
| .explorer-item:hover span { | |
| background: #000080; | |
| color: white; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- DESKTOP --> | |
| <div id="desktop"> | |
| <div class="desktop-icon" ondblclick="openWindow('myComputer')"> | |
| <i class="fa-solid fa-computer"></i> | |
| <span>My Computer</span> | |
| </div> | |
| <div class="desktop-icon" ondblclick="openWindow('recycle')"> | |
| <i class="fa-solid fa-trash-can"></i> | |
| <span>Recycle Bin</span> | |
| </div> | |
| <div class="desktop-icon" ondblclick="openWindow('notepad')"> | |
| <i class="fa-solid fa-note-sticky"></i> | |
| <span>Notepad</span> | |
| </div> | |
| <div class="desktop-icon" ondblclick="openWindow('minesweeper')"> | |
| <i class="fa-solid fa-bomb"></i> | |
| <span>Minesweeper</span> | |
| </div> | |
| <div class="desktop-icon" ondblclick="openWindow('internet')"> | |
| <i class="fa-brands fa-internet-explorer"></i> | |
| <span>Internet</span> | |
| </div> | |
| <div class="desktop-icon" ondblclick="openWindow('readme')"> | |
| <i class="fa-solid fa-file-lines"></i> | |
| <span>README.TXT</span> | |
| </div> | |
| </div> | |
| <!-- START MENU --> | |
| <div id="start-menu" class="win95-border"> | |
| <div class="sidebar-strip"><div class="sidebar-text">Windows 95</div></div> | |
| <div class="menu-items"> | |
| <div class="menu-item" onclick="openWindow('notepad')"><i class="fa-solid fa-note-sticky"></i>Programs</div> | |
| <div class="menu-item"><i class="fa-solid fa-folder-open"></i>Documents</div> | |
| <div class="menu-item"><i class="fa-solid fa-gear"></i>Settings</div> | |
| <div class="menu-item"><i class="fa-solid fa-magnifying-glass"></i>Find</div> | |
| <div class="menu-item"><i class="fa-solid fa-circle-question"></i>Help</div> | |
| <div class="menu-item" onclick="openWindow('run')"><i class="fa-solid fa-person-running"></i>Run...</div> | |
| <div class="menu-divider"></div> | |
| <div class="menu-item" onclick="alert('It is now safe to turn off your computer.')"><i class="fa-solid fa-power-off"></i>Shut Down...</div> | |
| </div> | |
| </div> | |
| <!-- WINDOW CONTAINER --> | |
| <div id="window-container"></div> | |
| <!-- TASKBAR --> | |
| <div id="taskbar"> | |
| <div id="start-btn" class="win95-btn" onclick="toggleStartMenu()"> | |
| <i class="fa-brands fa-windows"></i> Start | |
| </div> | |
| <div class="menu-divider" style="width: 2px; height: 20px; margin: 0 4px; border-left: 1px solid #808080; border-right: 1px solid #fff;"></div> | |
| <div id="task-list"> | |
| <!-- Task items go here --> | |
| </div> | |
| <div id="tray">12:00 PM</div> | |
| </div> | |
| <script> | |
| /* --- SYSTEM STATE --- */ | |
| let zIndexCounter = 100; | |
| const startMenu = document.getElementById('start-menu'); | |
| const startBtn = document.getElementById('start-btn'); | |
| const windowContainer = document.getElementById('window-container'); | |
| const taskList = document.getElementById('task-list'); | |
| /* --- CLOCK --- */ | |
| function updateClock() { | |
| const now = new Date(); | |
| let hours = now.getHours(); | |
| const minutes = now.getMinutes().toString().padStart(2, '0'); | |
| const ampm = hours >= 12 ? 'PM' : 'AM'; | |
| hours = hours % 12; | |
| hours = hours ? hours : 12; | |
| document.getElementById('tray').textContent = `${hours}:${minutes} ${ampm}`; | |
| } | |
| setInterval(updateClock, 1000); | |
| updateClock(); | |
| /* --- START MENU LOGIC --- */ | |
| function toggleStartMenu() { | |
| const isVisible = startMenu.style.display === 'block'; | |
| startMenu.style.display = isVisible ? 'none' : 'block'; | |
| startBtn.classList.toggle('active', !isVisible); | |
| } | |
| document.addEventListener('click', (e) => { | |
| if (!startMenu.contains(e.target) && !startBtn.contains(e.target)) { | |
| startMenu.style.display = 'none'; | |
| startBtn.classList.remove('active'); | |
| } | |
| }); | |
| /* --- WINDOW SYSTEM --- */ | |
| const apps = { | |
| 'notepad': { | |
| title: 'Untitled - Notepad', | |
| icon: 'fa-note-sticky', | |
| width: 400, | |
| height: 300, | |
| content: `<div class="win95-inset" style="height:100%;"><textarea class="notepad-area" placeholder="Type here..."></textarea></div>`, | |
| menu: true | |
| }, | |
| 'myComputer': { | |
| title: 'My Computer', | |
| icon: 'fa-computer', | |
| width: 500, | |
| height: 350, | |
| content: ` | |
| <div class="win95-inset explorer-body" style="height:100%;"> | |
| <div class="explorer-item"><i class="fa-solid fa-floppy-disk"></i><span>3Β½ Floppy (A:)</span></div> | |
| <div class="explorer-item"><i class="fa-solid fa-hard-drive"></i><span>(C:)</span></div> | |
| <div class="explorer-item"><i class="fa-solid fa-compact-disc"></i><span>(D:)</span></div> | |
| <div class="explorer-item"><i class="fa-solid fa-screwdriver-wrench"></i><span>Control Panel</span></div> | |
| <div class="explorer-item"><i class="fa-solid fa-print"></i><span>Printers</span></div> | |
| </div> | |
| `, | |
| menu: true | |
| }, | |
| 'recycle': { | |
| title: 'Recycle Bin', | |
| icon: 'fa-trash-can', | |
| width: 400, | |
| height: 300, | |
| content: `<div class="win95-inset explorer-body" style="height:100%;"><p style="width:100%; padding:10px;">The Recycle Bin is empty.</p></div>`, | |
| menu: true | |
| }, | |
| 'internet': { | |
| title: 'Internet Explorer', | |
| icon: 'fa-brands fa-internet-explorer', | |
| width: 600, | |
| height: 450, | |
| content: ` | |
| <div style="display:flex; flex-direction:column; height:100%;"> | |
| <div style="padding:4px; background:#c0c0c0; border-bottom:1px solid #808080;"> | |
| Address: <input type="text" value="http://www.microsoft.com" style="width:80%;"> | |
| </div> | |
| <div class="win95-inset" style="flex-grow:1; background:white; padding:20px; overflow:auto;"> | |
| <h1>Welcome to the Internet!</h1> | |
| <p>Under Construction.</p> | |
| <hr> | |
| <p style="color:blue; text-decoration:underline; cursor:pointer;">Click here to search</p> | |
| <br> | |
| <center><i class="fa-solid fa-triangle-exclamation" style="font-size:48px; color:#ffd700;"></i></center> | |
| </div> | |
| </div> | |
| `, | |
| menu: true | |
| }, | |
| 'readme': { | |
| title: 'README.TXT - Notepad', | |
| icon: 'fa-file-lines', | |
| width: 400, | |
| height: 300, | |
| content: `<div class="win95-inset" style="height:100%;"><textarea class="notepad-area">Welcome to Windows 95 Mockup! | |
| Created as a single HTML file artifact. | |
| Features: | |
| - Drag and Drop | |
| - Minesweeper Game | |
| - Functional Start Menu | |
| - Taskbar logic | |
| Enjoy the nostalgia! | |
| </textarea></div>`, | |
| menu: true | |
| }, | |
| 'minesweeper': { | |
| title: 'Minesweeper', | |
| icon: 'fa-bomb', | |
| width: 206, | |
| height: 300, // Auto adjusts | |
| menu: true, | |
| init: initMinesweeper, | |
| content: ` | |
| <div id="mine-container" style="background:#c0c0c0; padding:5px;"> | |
| <div class="minesweeper-header" class="win95-inset"> | |
| <div class="mine-lcd" id="mine-count">010</div> | |
| <button class="win95-btn mine-face" onclick="resetMinesweeper()"><i class="fa-solid fa-face-smile" style="color:#ffd700; text-shadow:1px 1px 0 #000;"></i></button> | |
| <div class="mine-lcd" id="mine-timer">000</div> | |
| </div> | |
| <div class="mine-grid" id="mine-grid"></div> | |
| </div> | |
| ` | |
| }, | |
| 'run': { | |
| title: 'Run', | |
| icon: 'fa-person-running', | |
| width: 350, | |
| height: 180, | |
| menu: false, | |
| content: ` | |
| <div style="padding:15px; display:flex; gap:10px; align-items:center;"> | |
| <i class="fa-solid fa-person-running" style="font-size:32px;"></i> | |
| <div> | |
| <p style="font-size:12px; margin:0 0 5px 0;">Type the name of a program, folder, document, or Internet resource, and Windows will open it for you.</p> | |
| <p style="font-size:12px;">Open: <input type="text" style="width:200px;"></p> | |
| </div> | |
| </div> | |
| <div style="display:flex; justify-content:flex-end; gap:5px; padding-right:10px;"> | |
| <button class="win95-btn" style="width:70px;" onclick="closeWindow(this.closest('.window').id)">OK</button> | |
| <button class="win95-btn" style="width:70px;" onclick="closeWindow(this.closest('.window').id)">Cancel</button> | |
| <button class="win95-btn" style="width:70px;">Browse...</button> | |
| </div> | |
| ` | |
| } | |
| }; | |
| function openWindow(appId) { | |
| const app = apps[appId]; | |
| if(!app) return; | |
| const id = 'win-' + Date.now(); | |
| const win = document.createElement('div'); | |
| win.className = 'window win95-border'; | |
| win.id = id; | |
| win.style.width = app.width + 'px'; | |
| win.style.height = app.height + 'px'; | |
| // Center randomish | |
| win.style.top = (50 + Math.random() * 50) + 'px'; | |
| win.style.left = (50 + Math.random() * 50) + 'px'; | |
| win.style.zIndex = ++zIndexCounter; | |
| // Menubar HTML | |
| const menuHtml = app.menu ? ` | |
| <div class="window-menu"> | |
| <span><u>F</u>ile</span> | |
| <span><u>E</u>dit</span> | |
| <span><u>V</u>iew</span> | |
| <span><u>H</u>elp</span> | |
| </div> | |
| ` : ''; | |
| win.innerHTML = ` | |
| <div class="title-bar" onmousedown="startDrag(event, '${id}')"> | |
| <div style="display:flex; align-items:center; gap:5px;"> | |
| <i class="fa-solid ${app.icon}"></i> ${app.title} | |
| </div> | |
| <div class="title-controls"> | |
| <button class="win95-btn control-btn" onclick="minimizeWindow('${id}')">_</button> | |
| <button class="win95-btn control-btn" onclick="maximizeWindow('${id}')">β‘</button> | |
| <button class="win95-btn control-btn" onclick="closeWindow('${id}')">X</button> | |
| </div> | |
| </div> | |
| ${menuHtml} | |
| <div class="window-content"> | |
| ${app.content} | |
| </div> | |
| `; | |
| // Focus listener | |
| win.addEventListener('mousedown', () => focusWindow(id)); | |
| windowContainer.appendChild(win); | |
| addTaskbarItem(id, app.title, app.icon); | |
| // Init scripts if any (like minesweeper) | |
| if(app.init) app.init(win); | |
| // Close start menu | |
| startMenu.style.display = 'none'; | |
| startBtn.classList.remove('active'); | |
| } | |
| function closeWindow(id) { | |
| const win = document.getElementById(id); | |
| if(win) win.remove(); | |
| const task = document.getElementById('task-' + id); | |
| if(task) task.remove(); | |
| } | |
| function focusWindow(id) { | |
| const win = document.getElementById(id); | |
| if(win) { | |
| win.style.zIndex = ++zIndexCounter; | |
| // Update taskbar styles | |
| document.querySelectorAll('.task-item').forEach(t => t.classList.remove('active')); | |
| const task = document.getElementById('task-' + id); | |
| if(task) task.classList.add('active'); | |
| // Visual title bar update | |
| document.querySelectorAll('.window').forEach(w => w.classList.add('inactive')); | |
| win.classList.remove('inactive'); | |
| } | |
| } | |
| function minimizeWindow(id) { | |
| const win = document.getElementById(id); | |
| win.style.display = 'none'; | |
| const task = document.getElementById('task-' + id); | |
| task.classList.remove('active'); | |
| } | |
| function maximizeWindow(id) { | |
| const win = document.getElementById(id); | |
| if(win.style.width === '100%') { | |
| // Restore (naive implementation, resets to default) | |
| win.style.width = apps[Object.keys(apps).find(key => win.innerHTML.includes(apps[key].title))].width + 'px'; // Hacky finding original size | |
| win.style.height = 'auto'; | |
| win.style.top = '50px'; | |
| win.style.left = '50px'; | |
| } else { | |
| win.style.width = '100%'; | |
| win.style.height = 'calc(100% - 32px)'; // Minus taskbar | |
| win.style.top = '0'; | |
| win.style.left = '0'; | |
| } | |
| } | |
| function addTaskbarItem(id, title, icon) { | |
| const item = document.createElement('div'); | |
| item.className = 'task-item win95-btn active'; | |
| item.id = 'task-' + id; | |
| item.innerHTML = `<i class="fa-solid ${icon}"></i> ${title}`; | |
| item.onclick = () => { | |
| const win = document.getElementById(id); | |
| if (win.style.display === 'none') { | |
| win.style.display = 'flex'; | |
| focusWindow(id); | |
| } else if (item.classList.contains('active')) { | |
| minimizeWindow(id); | |
| } else { | |
| focusWindow(id); | |
| } | |
| }; | |
| taskList.appendChild(item); | |
| } | |
| /* --- DRAGGING --- */ | |
| let isDragging = false; | |
| let dragOffsetX = 0; | |
| let dragOffsetY = 0; | |
| let draggedWindow = null; | |
| function startDrag(e, id) { | |
| if(e.target.tagName === 'BUTTON') return; // Don't drag if clicking controls | |
| isDragging = true; | |
| draggedWindow = document.getElementById(id); | |
| focusWindow(id); | |
| const rect = draggedWindow.getBoundingClientRect(); | |
| dragOffsetX = e.clientX - rect.left; | |
| dragOffsetY = e.clientY - rect.top; | |
| } | |
| document.addEventListener('mousemove', (e) => { | |
| if (isDragging && draggedWindow) { | |
| draggedWindow.style.left = (e.clientX - dragOffsetX) + 'px'; | |
| draggedWindow.style.top = (e.clientY - dragOffsetY) + 'px'; | |
| } | |
| }); | |
| document.addEventListener('mouseup', () => { | |
| isDragging = false; | |
| draggedWindow = null; | |
| }); | |
| /* --- MINESWEEPER LOGIC --- */ | |
| let mineGrid = []; | |
| let mineGameOver = false; | |
| function initMinesweeper(win) { | |
| resetMinesweeper(); | |
| } | |
| function resetMinesweeper() { | |
| const gridEl = document.getElementById('mine-grid'); | |
| if(!gridEl) return; | |
| gridEl.innerHTML = ''; | |
| mineGrid = []; | |
| mineGameOver = false; | |
| document.querySelector('.mine-face i').className = "fa-solid fa-face-smile"; | |
| // Initialize 9x9 | |
| for(let i=0; i<81; i++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'mine-cell'; | |
| cell.dataset.idx = i; | |
| cell.onclick = () => clickMineCell(i); | |
| cell.oncontextmenu = (e) => { e.preventDefault(); flagMineCell(i); }; | |
| gridEl.appendChild(cell); | |
| mineGrid.push({ isBomb: false, revealed: false, flagged: false, neighbors: 0, el: cell }); | |
| } | |
| // Place 10 bombs | |
| let bombsPlaced = 0; | |
| while(bombsPlaced < 10) { | |
| let idx = Math.floor(Math.random() * 81); | |
| if(!mineGrid[idx].isBomb) { | |
| mineGrid[idx].isBomb = true; | |
| bombsPlaced++; | |
| } | |
| } | |
| // Calculate numbers | |
| for(let i=0; i<81; i++) { | |
| if(mineGrid[i].isBomb) continue; | |
| let neighbors = 0; | |
| const r = Math.floor(i/9); | |
| const c = i%9; | |
| for(let dr=-1; dr<=1; dr++) { | |
| for(let dc=-1; dc<=1; dc++) { | |
| const nr = r+dr, nc = c+dc; | |
| if(nr>=0 && nr<9 && nc>=0 && nc<9) { | |
| if(mineGrid[nr*9+nc].isBomb) neighbors++; | |
| } | |
| } | |
| } | |
| mineGrid[i].neighbors = neighbors; | |
| } | |
| } | |
| function clickMineCell(idx) { | |
| if(mineGameOver || mineGrid[idx].revealed || mineGrid[idx].flagged) return; | |
| const cell = mineGrid[idx]; | |
| cell.revealed = true; | |
| cell.el.classList.add('revealed'); | |
| if(cell.isBomb) { | |
| cell.el.classList.add('bomb'); | |
| cell.el.innerHTML = '<i class="fa-solid fa-bomb"></i>'; | |
| mineGameOver = true; | |
| document.querySelector('.mine-face i').className = "fa-solid fa-face-dizzy"; | |
| revealAll(); | |
| } else { | |
| if(cell.neighbors > 0) { | |
| cell.el.textContent = cell.neighbors; | |
| // Colors | |
| const colors = ['blue', 'green', 'red', 'purple', 'maroon', 'turquoise', 'black', 'gray']; | |
| cell.el.style.color = colors[cell.neighbors-1]; | |
| } else { | |
| // Flood fill | |
| const r = Math.floor(idx/9); | |
| const c = idx%9; | |
| for(let dr=-1; dr<=1; dr++) { | |
| for(let dc=-1; dc<=1; dc++) { | |
| const nr = r+dr, nc = c+dc; | |
| if(nr>=0 && nr<9 && nc>=0 && nc<9) { | |
| clickMineCell(nr*9+nc); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| function flagMineCell(idx) { | |
| if(mineGameOver || mineGrid[idx].revealed) return; | |
| const cell = mineGrid[idx]; | |
| cell.flagged = !cell.flagged; | |
| cell.el.innerHTML = cell.flagged ? '<i class="fa-solid fa-flag" style="color:red;"></i>' : ''; | |
| } | |
| function revealAll() { | |
| mineGrid.forEach(c => { | |
| if(c.isBomb) { | |
| c.el.classList.add('revealed', 'bomb'); | |
| c.el.innerHTML = '<i class="fa-solid fa-bomb"></i>'; | |
| } | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This file contains a full Windows 95 simulation with a working Minesweeper game.
π If you are reading this on GitHub: You are just seeing raw code because GitHub forces text/plain headers.
π’ To see it LIVE (The Hack): Click here to render it as a real website using GistHost: https://gisthost.github.io/?91fed5757e310bda0ac3fc83dc34b7b2
This demonstrates Hack #1 from my blog: turning a static Gist into a live web host.