Skip to content

Commit 1fb67d8

Browse files
authored
Create index.html
1 parent 47d27c0 commit 1fb67d8

1 file changed

Lines changed: 309 additions & 0 deletions

File tree

apps/sea-bean/index.html

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
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

Comments
 (0)