|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="UTF-8"> |
| 5 | + <title>Sea Ban - Games</title> |
| 6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 7 | + <link rel="icon" id="favicon" href=""> |
| 8 | + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"/> |
| 9 | + <style> |
| 10 | + /* --- THEME VARIABLES --- */ |
| 11 | + :root { |
| 12 | + --accent: #00d2ff; |
| 13 | + --accent-dark: #0077ff; |
| 14 | + --bg-deep: #000814; |
| 15 | + --bg-light: #001f3f; |
| 16 | + --grid-size: 220px; |
| 17 | + } |
| 18 | + |
| 19 | + /* FORCE DEEP SEA THEME */ |
| 20 | + html, body { |
| 21 | + background-color: var(--bg-deep) !important; |
| 22 | + margin: 0; padding: 0; |
| 23 | + height: 100vh; width: 100vw; |
| 24 | + overflow: hidden; |
| 25 | + font-family: 'Inter', sans-serif; |
| 26 | + color: white; |
| 27 | + transition: background-color 0.5s ease; |
| 28 | + } |
| 29 | + |
| 30 | + body { |
| 31 | + background: radial-gradient(circle at center, var(--bg-light), var(--bg-deep)); |
| 32 | + background-image: |
| 33 | + linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px), |
| 34 | + linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px); |
| 35 | + background-size: 50px 50px; |
| 36 | + } |
| 37 | + |
| 38 | + /* STARTUP PICKER */ |
| 39 | + #startupOverlay { position: fixed; inset: 0; background: rgba(0, 5, 15, 0.95); display: none; align-items: center; justify-content: center; z-index: 5000; backdrop-filter: blur(15px); } |
| 40 | + .startup-box { background: rgba(10, 15, 30, 0.95); padding: 40px; border-radius: 24px; display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 20px; border: 1px solid var(--accent-dark); width: 90%; max-width: 600px; box-shadow: 0 20px 50px rgba(0,0,0,0.8); } |
| 41 | + .startup-btn { padding: 20px; border-radius: 16px; cursor: pointer; text-align: center; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); transition: .25s; } |
| 42 | + .startup-btn:hover { background: var(--accent-dark); transform: translateY(-5px); color: #fff; box-shadow: 0 5px 20px var(--accent); border-color: var(--accent); } |
| 43 | + |
| 44 | + /* TOP BAR */ |
| 45 | + .top-bar-container { position: fixed; top: 15px; left: 0; right: 0; display: flex; flex-direction: column; align-items: center; z-index: 20; gap: 8px; pointer-events: none; } |
| 46 | + .search-bar { pointer-events: auto; height: 50px; padding: 0 18px; display: flex; align-items: center; gap: 12px; background: rgba(0, 0, 0, 0.5); border-radius: 16px; border: 1px solid rgba(255, 255, 255, 0.15); backdrop-filter: blur(12px); transition: border-color 0.3s; } |
| 47 | + .search-bar:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0, 210, 255, 0.15); } |
| 48 | + .search-bar input { background: none; border: none; outline: none; color: white; font-size: 16px; width: 250px; } |
| 49 | + #liveCount { font-size: 12px; background: var(--accent); color: #000; padding: 2px 8px; border-radius: 8px; font-weight: 800; transition: background 0.5s; } |
| 50 | + #sysStatus { font-size: 10px; color: var(--accent); text-transform: uppercase; letter-spacing: 2px; font-weight: bold; transition: color 0.5s; } |
| 51 | + |
| 52 | + /* SETTINGS */ |
| 53 | + #settingsBtn { position: fixed; top: 20px; right: 25px; width: 45px; height: 45px; border-radius: 12px; background: rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.15); display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 30; transition: all 0.3s; } |
| 54 | + #settingsBtn:hover { border-color: var(--accent); color: var(--accent); } |
| 55 | + #settingsOverlay { position: fixed; inset: 0; background: rgba(0,0,0,.7); backdrop-filter: blur(8px); display: none; align-items: center; justify-content: center; z-index: 2000; } |
| 56 | + .settings-panel { width: 340px; padding: 25px; border-radius: 18px; background: rgba(10, 15, 30, 0.95); border: 1px solid rgba(255, 255, 255, 0.1); border-top: 4px solid var(--accent); display: flex; flex-direction: column; gap: 20px; transition: border-top-color 0.5s; } |
| 57 | + |
| 58 | + /* DATA GRID */ |
| 59 | + #container { position: fixed; inset: 0; display: grid; grid-template-columns: repeat(auto-fill, var(--grid-size)); justify-content: center; gap: 20px; padding: 130px 30px 50px 30px; overflow-y: auto; } |
| 60 | + #container::-webkit-scrollbar { display: none; } |
| 61 | + .zone-item { width: var(--grid-size); height: 175px; border-radius: 20px; position: relative; overflow: hidden; background: rgba(255, 255, 255, 0.05); cursor: pointer; transition: .3s; border: 1px solid rgba(255, 255, 255, 0.1); } |
| 62 | + .zone-item:hover { transform: translateY(-8px) scale(1.02); border-color: var(--accent); box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); } |
| 63 | + .zone-item img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; filter: brightness(0.6); transition: 0.3s; } |
| 64 | + .zone-item:hover img { filter: brightness(0.9); } |
| 65 | + .zone-item b { position: absolute; bottom: 12px; left: 14px; color: white; font-size: 14px; text-shadow: 0 2px 10px #000; width: 85%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } |
| 66 | + |
| 67 | + /* GAME VIEWER & LOADER */ |
| 68 | + #zoneViewer { position: fixed; inset: 0; background: #000; display: none; flex-direction: column; z-index: 6000; } |
| 69 | + #zoneFrame { flex: 1; border: none; background: #fff; } |
| 70 | + #closeBtn { position: absolute; top: 20px; right: 25px; width: 45px; height: 45px; border-radius: 12px; background: #ff4d4d; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 6001; color: white; border: 1px solid rgba(255,255,255,0.2); transition: 0.3s; } |
| 71 | + #closeBtn:hover { background: #ff1a1a; transform: scale(1.05); } |
| 72 | + |
| 73 | + #loaderBar { |
| 74 | + position: absolute; |
| 75 | + top: 0; left: 0; |
| 76 | + width: 0%; height: 4px; |
| 77 | + background: var(--accent); |
| 78 | + box-shadow: 0 0 15px var(--accent); |
| 79 | + transition: width 0.2s ease, opacity 0.3s ease; |
| 80 | + z-index: 6005; |
| 81 | + pointer-events: none; |
| 82 | + } |
| 83 | + |
| 84 | + /* Settings Inputs */ |
| 85 | + select { padding: 12px; background: rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.15); color: white; border-radius: 8px; outline: none; font-family: 'Inter', sans-serif; cursor: pointer; width: 100%; } |
| 86 | + select:focus { border-color: var(--accent); } |
| 87 | + </style> |
| 88 | +</head> |
| 89 | +<body> |
| 90 | + |
| 91 | +<div id="startupOverlay"> |
| 92 | + <div class="startup-box" id="startupBox"></div> |
| 93 | +</div> |
| 94 | + |
| 95 | +<div class="top-bar-container"> |
| 96 | + <div id="sysStatus"></div> |
| 97 | + <div class="search-bar"> |
| 98 | + <i class="fas fa-search" style="opacity: 0.5"></i> |
| 99 | + <input id="searchBar" placeholder="Search the depths..." oninput="filterZones()"> |
| 100 | + <span id="liveCount">0</span> |
| 101 | + </div> |
| 102 | +</div> |
| 103 | + |
| 104 | +<div id="settingsBtn" onclick="toggleSettings()"><i class="fas fa-gear"></i></div> |
| 105 | + |
| 106 | +<div id="settingsOverlay" onclick="toggleSettings(event)"> |
| 107 | + <div class="settings-panel" onclick="event.stopPropagation()"> |
| 108 | + <h3 style="margin-top:0; color: white; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 10px;">Archive Source</h3> |
| 109 | + <div class="setting-row"> |
| 110 | + <select id="sourceSelect" onchange="toggleSource(this.value)"></select> |
| 111 | + </div> |
| 112 | + </div> |
| 113 | +</div> |
| 114 | + |
| 115 | +<div id="container"></div> |
| 116 | + |
| 117 | +<div id="zoneViewer"> |
| 118 | + <div id="loaderBar"></div> |
| 119 | + <div id="closeBtn" onclick="closeViewer()"><i class="fas fa-times"></i></div> |
| 120 | + <iframe id="zoneFrame"></iframe> |
| 121 | +</div> |
| 122 | + |
| 123 | +<div id="themeGridContainer" style="display: none;"></div> |
| 124 | +<div id="themeTransitionOverlay" style="display: none;"><div id="themeWaveContainer"><g id="themeWaveParallax"></g></div></div> |
| 125 | + |
| 126 | +<script> |
| 127 | + const sources = [ |
| 128 | + { name: "GN Math", type: "json", json: "https://cdn.jsdelivr.net/gh/gn-math/assets@latest/zones.json", cover: "https://cdn.jsdelivr.net/gh/gn-math/covers@main/", html: "https://cdn.jsdelivr.net/gh/gn-math/html@main/" }, |
| 129 | + { name: "Sea Bean", type: "json", json: "https://cdn.jsdelivr.net/gh/sea-bean-unblocked/sde@main/zzz.json", cover: "https://cdn.jsdelivr.net/gh/sea-bean-unblocked/Singlemile@main/Icon/", html: "https://cdn.jsdelivr.net/gh/sea-bean-unblocked/Singlemile@main/games/" }, |
| 130 | + { |
| 131 | + name: "Master Archive", |
| 132 | + type: "combined", |
| 133 | + repos: ["sea-bean-unblocked/Folder-1", "sea-bean-unblocked/Folder-2", "sea-bean-unblocked/Folder-3"], |
| 134 | + icon: "https://sea-math-official.neocities.org/Perfet.png" |
| 135 | + } |
| 136 | + ]; |
| 137 | + |
| 138 | + let zones = []; |
| 139 | + let currentSource = 0; |
| 140 | + |
| 141 | + window.createSplash = function() {}; |
| 142 | + |
| 143 | + function cleanDataProcess(rawFiles) { |
| 144 | + const seen = new Set(); |
| 145 | + const processed = rawFiles.map(file => { |
| 146 | + let cleanName = file.name.replace(/\.html$/i, '').replace(/[-_]/g, ' ').replace(/\b\w/g, char => char.toUpperCase()); |
| 147 | + if (seen.has(cleanName)) return null; |
| 148 | + seen.add(cleanName); |
| 149 | + return { ...file, name: cleanName }; |
| 150 | + }).filter(item => item !== null); |
| 151 | + processed.sort((a, b) => a.name.localeCompare(b.name)); |
| 152 | + return processed; |
| 153 | + } |
| 154 | + |
| 155 | + async function init() { |
| 156 | + const startupBox = document.getElementById("startupBox"); |
| 157 | + const sourceSelect = document.getElementById("sourceSelect"); |
| 158 | + startupBox.innerHTML = ""; sourceSelect.innerHTML = ""; |
| 159 | + |
| 160 | + sources.forEach((s, i) => { |
| 161 | + const btn = document.createElement("div"); |
| 162 | + btn.className = "startup-btn"; |
| 163 | + btn.innerHTML = `<b>${s.name}</b><br><span style="font-size: 12px; opacity: 0.7;">Select Archive</span>`; |
| 164 | + btn.onclick = () => chooseStartup(i); |
| 165 | + startupBox.appendChild(btn); |
| 166 | + const opt = document.createElement("option"); opt.value = i; opt.innerText = s.name; |
| 167 | + sourceSelect.appendChild(opt); |
| 168 | + }); |
| 169 | + |
| 170 | + const savedSource = localStorage.getItem("sourceIndex"); |
| 171 | + if (savedSource !== null) { |
| 172 | + currentSource = parseInt(savedSource); |
| 173 | + sourceSelect.value = currentSource; |
| 174 | + loadSource(); |
| 175 | + } else { |
| 176 | + document.getElementById("startupOverlay").style.display = "flex"; |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + async function loadSource() { |
| 181 | + const statusText = document.getElementById("sysStatus"); |
| 182 | + const s = sources[currentSource]; |
| 183 | + statusText.innerText = "INDEXING LIBRARY..."; |
| 184 | + |
| 185 | + try { |
| 186 | + if (s.type === "json") { |
| 187 | + const r = await fetch(s.json); |
| 188 | + zones = await r.json(); |
| 189 | + } else if (s.type === "combined") { |
| 190 | + const results = await Promise.all(s.repos.map(async (repo) => { |
| 191 | + const r = await fetch(`https://api.github.com/repos/${repo}/contents/`); |
| 192 | + const data = await r.json(); |
| 193 | + return data.filter(f => f.name.endsWith('.html')).map(f => ({ |
| 194 | + name: f.name, url: `https://cdn.jsdelivr.net/gh/${repo}@main/${f.name}`, isDirect: true, cover: s.icon |
| 195 | + })); |
| 196 | + })); |
| 197 | + zones = cleanDataProcess(results.flat()); |
| 198 | + } |
| 199 | + |
| 200 | + statusText.innerText = "SYSTEM ACTIVE"; |
| 201 | + setTimeout(() => statusText.innerText = "", 2000); |
| 202 | + displayZones(zones); |
| 203 | + } catch (err) { |
| 204 | + statusText.innerText = "ERROR FETCHING ARCHIVE"; |
| 205 | + console.error(err); |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + function displayZones(list) { |
| 210 | + const container = document.getElementById("container"); |
| 211 | + container.innerHTML = ""; |
| 212 | + document.getElementById("liveCount").innerText = list.length; |
| 213 | + const s = sources[currentSource]; |
| 214 | + |
| 215 | + list.forEach(z => { |
| 216 | + const d = document.createElement("div"); |
| 217 | + d.className = "zone-item"; |
| 218 | + d.onclick = () => openZone(z); |
| 219 | + const img = document.createElement("img"); |
| 220 | + if (s.type === "combined") { img.src = z.cover; } |
| 221 | + else { |
| 222 | + let rc = z.cover || ""; |
| 223 | + img.src = rc.includes("{COVER_URL}") ? rc.replace("{COVER_URL}", s.cover) : s.cover + rc.replace(/^\//, ""); |
| 224 | + } |
| 225 | + img.onerror = () => img.src = "https://via.placeholder.com/220x175/001122/00d2ff?text=Game"; |
| 226 | + const b = document.createElement("b"); b.textContent = z.name; |
| 227 | + d.append(img, b); |
| 228 | + container.appendChild(d); |
| 229 | + }); |
| 230 | + } |
| 231 | + |
| 232 | + function filterZones() { |
| 233 | + const q = document.getElementById("searchBar").value.toLowerCase(); |
| 234 | + displayZones(zones.filter(z => z.name && z.name.toLowerCase().includes(q))); |
| 235 | + } |
| 236 | + |
| 237 | + // --- YOUR PREFERRED LOADER LOGIC --- |
| 238 | + function openZone(z) { |
| 239 | + const s = sources[currentSource]; |
| 240 | + let finalURL = z.isDirect ? z.url : (z.url.includes("{HTML_URL}") ? z.url.replace("{HTML_URL}", s.html) : s.html + z.url.replace(/^\//, "")); |
| 241 | + |
| 242 | + // Prevent CDN Plain-Text Error |
| 243 | + if (finalURL.includes("raw.githubusercontent.com")) { |
| 244 | + finalURL = finalURL.replace("raw.githubusercontent.com", "cdn.jsdelivr.net/gh").replace("/main/", "@main/").replace("/master/", "@master/"); |
| 245 | + } |
| 246 | + |
| 247 | + document.getElementById("zoneViewer").style.display = "flex"; |
| 248 | + |
| 249 | + // Reset and show loader bar |
| 250 | + document.getElementById("loaderBar").style.opacity = "1"; |
| 251 | + document.getElementById("loaderBar").style.width = "40%"; |
| 252 | + |
| 253 | + fetch(finalURL).then(r => r.text()).then(html => { |
| 254 | + document.getElementById("loaderBar").style.width = "100%"; |
| 255 | + const doc = document.getElementById("zoneFrame").contentWindow.document; |
| 256 | + doc.open(); |
| 257 | + doc.write(html); |
| 258 | + doc.close(); |
| 259 | + |
| 260 | + // Fade out and reset after writing |
| 261 | + setTimeout(() => { |
| 262 | + document.getElementById("loaderBar").style.opacity = "0"; |
| 263 | + setTimeout(() => document.getElementById("loaderBar").style.width = "0%", 400); |
| 264 | + }, 300); |
| 265 | + |
| 266 | + }).catch(() => { |
| 267 | + // If fetch is blocked, try direct iframe src |
| 268 | + document.getElementById("zoneFrame").src = finalURL; |
| 269 | + document.getElementById("loaderBar").style.width = "100%"; |
| 270 | + |
| 271 | + setTimeout(() => { |
| 272 | + document.getElementById("loaderBar").style.opacity = "0"; |
| 273 | + setTimeout(() => document.getElementById("loaderBar").style.width = "0%", 400); |
| 274 | + }, 300); |
| 275 | + }); |
| 276 | + } |
| 277 | + |
| 278 | + function chooseStartup(idx) { |
| 279 | + currentSource = idx; |
| 280 | + localStorage.setItem("sourceIndex", idx); |
| 281 | + document.getElementById("startupOverlay").style.display = "none"; |
| 282 | + loadSource(); |
| 283 | + } |
| 284 | + |
| 285 | + function toggleSettings(e) { |
| 286 | + const s = document.getElementById("settingsOverlay"); |
| 287 | + if (e && e.target !== s) return; |
| 288 | + s.style.display = s.style.display === "flex" ? "none" : "flex"; |
| 289 | + } |
| 290 | + |
| 291 | + function toggleSource(v) { |
| 292 | + currentSource = parseInt(v); |
| 293 | + localStorage.setItem("sourceIndex", v); |
| 294 | + document.getElementById("container").innerHTML = ""; |
| 295 | + loadSource(); |
| 296 | + } |
| 297 | + |
| 298 | + function closeViewer() { |
| 299 | + document.getElementById("zoneViewer").style.display = "none"; |
| 300 | + document.getElementById("zoneFrame").src = "about:blank"; |
| 301 | + // Reset loader silently |
| 302 | + document.getElementById("loaderBar").style.width = "0%"; |
| 303 | + } |
| 304 | + |
| 305 | + window.onload = init; |
| 306 | +</script> |
| 307 | + |
| 308 | +</body> |
| 309 | +</html> |
0 commit comments