mlv2boii 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.html +47 -36
- package/loader.svg +36 -29
- package/package.json +1 -1
package/index.html
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
<
|
|
3
|
-
<div xmlns="http://www.w3.org/1999/xhtml">
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
4
3
|
<head>
|
|
5
4
|
<meta charset="UTF-8" />
|
|
6
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
@@ -71,13 +70,19 @@
|
|
|
71
70
|
@keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }
|
|
72
71
|
.skel { background: linear-gradient(100deg, #18181c 30%, #202024 45%, #18181c 60%); background-size: 200% 100%; animation: shimmer 1.6s ease-in-out infinite; }
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
[data-fx] { will-change: transform, opacity; }
|
|
74
|
+
|
|
75
|
+
.hero-orb { animation: heroFloat 7s ease-in-out infinite; }
|
|
76
|
+
.hero-line { animation: heroLine 2.8s ease-in-out infinite; }
|
|
77
|
+
@keyframes heroFloat {
|
|
78
|
+
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
|
|
79
|
+
50% { transform: translate3d(0, 12px, 0) scale(1.04); }
|
|
80
|
+
}
|
|
81
|
+
@keyframes heroLine {
|
|
82
|
+
0%, 100% { opacity: .35; transform: scaleX(.7); }
|
|
83
|
+
50% { opacity: 1; transform: scaleX(1); }
|
|
84
|
+
}
|
|
85
|
+
</style>
|
|
81
86
|
</head>
|
|
82
87
|
|
|
83
88
|
<body class="bg-ink-950 text-ink-100 font-sans antialiased min-h-screen">
|
|
@@ -90,7 +95,7 @@
|
|
|
90
95
|
<a href="#" class="flex items-center gap-2 shrink-0 group">
|
|
91
96
|
<span class="relative h-2 w-2">
|
|
92
97
|
<span class="absolute inset-0 rounded-full bg-red-500"></span>
|
|
93
|
-
|
|
98
|
+
<span class="absolute inset-0 animate-ping rounded-full bg-red-500 opacity-60"></span>
|
|
94
99
|
</span>
|
|
95
100
|
<span class="font-display font-semibold tracking-tight text-[15px] leading-none">mlv2</span>
|
|
96
101
|
</a>
|
|
@@ -107,12 +112,27 @@
|
|
|
107
112
|
</div>
|
|
108
113
|
|
|
109
114
|
<div class="border-t border-ink-800/60">
|
|
110
|
-
|
|
115
|
+
<div id="category-bar" class="mx-auto flex max-w-5xl items-center justify-center gap-1.5 overflow-x-auto px-4 py-2.5 sm:px-6"></div>
|
|
111
116
|
</div>
|
|
112
117
|
</header>
|
|
113
118
|
|
|
114
119
|
<!-- ================= MAIN ================= -->
|
|
115
|
-
<main class="relative z-10 mx-auto max-w-
|
|
120
|
+
<main class="relative z-10 mx-auto max-w-5xl px-4 sm:px-6 py-8 sm:py-12">
|
|
121
|
+
|
|
122
|
+
<!-- ================= HERO ================= -->
|
|
123
|
+
<section class="relative mx-auto mb-10 max-w-2xl overflow-hidden rounded-[2rem] border border-ink-700/70 bg-ink-900/65 px-5 py-10 text-center shadow-[0_24px_80px_-40px_rgba(232,20,47,0.5)] sm:px-10 sm:py-14">
|
|
124
|
+
<div class="hero-orb pointer-events-none absolute -right-20 -top-24 h-64 w-64 rounded-full bg-red-500/10 blur-3xl"></div>
|
|
125
|
+
<div class="hero-orb pointer-events-none absolute -bottom-28 -left-20 h-56 w-56 rounded-full bg-red-700/10 blur-3xl" style="animation-delay: -3.5s"></div>
|
|
126
|
+
<div class="relative">
|
|
127
|
+
<div class="mx-auto mb-5 flex w-fit items-center gap-2 rounded-full border border-red-500/25 bg-red-500/[0.07] px-3 py-1 text-[10px] font-mono uppercase tracking-[0.22em] text-red-300">
|
|
128
|
+
<span class="h-1.5 w-1.5 rounded-full bg-red-400 shadow-[0_0_12px_rgba(255,77,97,0.9)]"></span>
|
|
129
|
+
Play something
|
|
130
|
+
</div>
|
|
131
|
+
<h1 class="font-display text-4xl font-semibold tracking-[-0.05em] text-ink-50 sm:text-6xl">Your next <span class="text-red-400">favorite</span> game.</h1>
|
|
132
|
+
<p class="mx-auto mt-4 max-w-md text-sm leading-6 text-ink-300 sm:text-[15px]">A curated shelf of quick escapes, hidden gems, and games worth one more round.</p>
|
|
133
|
+
<div class="hero-line mx-auto mt-7 h-px w-24 bg-gradient-to-r from-transparent via-red-400 to-transparent"></div>
|
|
134
|
+
</div>
|
|
135
|
+
</section>
|
|
116
136
|
|
|
117
137
|
<!-- setup banner, shown only while CONFIG still has placeholder values -->
|
|
118
138
|
<div id="setup-banner" class="hidden mb-8 rounded-xl border border-red-500/25 bg-red-500/[0.06] px-4 py-3.5">
|
|
@@ -121,7 +141,7 @@
|
|
|
121
141
|
</div>
|
|
122
142
|
|
|
123
143
|
<!-- skeleton loading state -->
|
|
124
|
-
<div id="skeleton-grid" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5
|
|
144
|
+
<div id="skeleton-grid" class="grid grid-cols-2 gap-3.5 sm:grid-cols-3 sm:gap-4 md:grid-cols-4 lg:grid-cols-5">
|
|
125
145
|
</div>
|
|
126
146
|
|
|
127
147
|
<!-- empty state -->
|
|
@@ -134,7 +154,7 @@
|
|
|
134
154
|
</div>
|
|
135
155
|
|
|
136
156
|
<!-- game grid -->
|
|
137
|
-
<div id="grid" class="hidden grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5
|
|
157
|
+
<div id="grid" class="hidden grid grid-cols-2 gap-3.5 sm:grid-cols-3 sm:gap-4 md:grid-cols-4 lg:grid-cols-5"></div>
|
|
138
158
|
</main>
|
|
139
159
|
|
|
140
160
|
<!-- ================= FOOTER ================= -->
|
|
@@ -312,20 +332,12 @@ const isConfigured = () =>
|
|
|
312
332
|
!CONFIG.databaseId.includes("YOUR_");
|
|
313
333
|
|
|
314
334
|
/* ---------------------------------------------------------------------
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const reduceMotionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
322
|
-
let reduceMotion = reduceMotionQuery.matches;
|
|
323
|
-
reduceMotionQuery.addEventListener("change", (e) => (reduceMotion = e.matches));
|
|
324
|
-
|
|
325
|
-
function fx(targets, keyframes, options = {}) {
|
|
326
|
-
if (reduceMotion) return animate(targets, keyframes, { duration: 0 });
|
|
327
|
-
return animate(targets, keyframes, options);
|
|
328
|
-
}
|
|
335
|
+
Animation helper. Motion is intentionally kept consistent for every
|
|
336
|
+
visitor so the interface has the same visual rhythm across devices.
|
|
337
|
+
--------------------------------------------------------------------- */
|
|
338
|
+
function fx(targets, keyframes, options = {}) {
|
|
339
|
+
return animate(targets, keyframes, options);
|
|
340
|
+
}
|
|
329
341
|
|
|
330
342
|
/* ------------------------------- state ------------------------------- */
|
|
331
343
|
let allGames = [];
|
|
@@ -435,10 +447,11 @@ function renderGrid() {
|
|
|
435
447
|
grid.innerHTML = games
|
|
436
448
|
.map((g) => {
|
|
437
449
|
const src = iconSrcFor(g) || monogramSrc(g.name);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
450
|
+
return `<button data-fx data-id="${g.$id}" class="group relative aspect-square overflow-hidden rounded-2xl border border-ink-700/70 bg-ink-900 text-left opacity-0 shadow-[0_12px_28px_-18px_rgba(0,0,0,0.9)] transition-[border-color,box-shadow,transform] duration-300 hover:-translate-y-1 hover:border-red-400/65 hover:shadow-[0_0_0_1px_rgba(232,20,47,0.35),0_18px_32px_-14px_rgba(232,20,47,0.55)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500/60">
|
|
451
|
+
<img src="${src}" alt="${escapeHtml(g.name)}" loading="lazy" class="h-full w-full object-cover transition-transform duration-700 ease-out group-hover:scale-[1.09]" onerror="this.onerror=null;this.src='${monogramSrc(g.name)}'" />
|
|
452
|
+
<div class="absolute inset-0 bg-gradient-to-t from-ink-950/95 via-ink-950/10 to-transparent opacity-90 transition-opacity duration-300 group-hover:opacity-100"></div>
|
|
453
|
+
<div class="absolute inset-x-0 bottom-0 p-3">
|
|
454
|
+
<p class="truncate text-[12.5px] font-medium text-ink-50 leading-tight">${escapeHtml(g.name)}</p>
|
|
442
455
|
${g.category ? `<p class="truncate mt-0.5 text-[9.5px] font-mono uppercase tracking-wide text-ink-400">${escapeHtml(g.category)}</p>` : ""}
|
|
443
456
|
</div>
|
|
444
457
|
</button>`;
|
|
@@ -732,6 +745,4 @@ el("add-game-form").addEventListener("submit", async (e) => {
|
|
|
732
745
|
});
|
|
733
746
|
</script>
|
|
734
747
|
</body>
|
|
735
|
-
</
|
|
736
|
-
</foreignObject>
|
|
737
|
-
</svg>
|
|
748
|
+
</html>
|
package/loader.svg
CHANGED
|
@@ -1,16 +1,6 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
height="100vh"
|
|
5
|
-
viewBox="0 0 100 100"
|
|
6
|
-
preserveAspectRatio="none"
|
|
7
|
-
style="position:fixed;top:0;left:0;width:100vw;height:100vh;margin:0;padding:0;">
|
|
8
|
-
|
|
9
|
-
<foreignObject x="0" y="0" width="100" height="100">
|
|
10
|
-
|
|
11
|
-
<xhtml:html xmlns="http://www.w3.org/1999/xhtml"
|
|
12
|
-
style="width:100%;height:100%;margin:0;padding:0;">
|
|
13
|
-
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xhtml="http://www.w3.org/1999/xhtml" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none">
|
|
2
|
+
<foreignObject height="100%" width="100%" y="0" x="0" id="fo">
|
|
3
|
+
<xhtml:html style="width:100%;height:100%;margin:0;padding:0;" xmlns="http://www.w3.org/1999/xhtml">
|
|
14
4
|
<head>
|
|
15
5
|
<style>
|
|
16
6
|
html, body {
|
|
@@ -23,11 +13,11 @@
|
|
|
23
13
|
}
|
|
24
14
|
|
|
25
15
|
iframe {
|
|
26
|
-
position:
|
|
16
|
+
position: absolute;
|
|
27
17
|
top: 0;
|
|
28
18
|
left: 0;
|
|
29
|
-
width:
|
|
30
|
-
height:
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 100%;
|
|
31
21
|
border: 0;
|
|
32
22
|
margin: 0;
|
|
33
23
|
padding: 0;
|
|
@@ -37,39 +27,56 @@
|
|
|
37
27
|
</head>
|
|
38
28
|
|
|
39
29
|
<body>
|
|
30
|
+
|
|
40
31
|
<iframe id="mlv2" src="about:blank"></iframe>
|
|
41
32
|
|
|
42
33
|
<script>
|
|
43
34
|
//<![CDATA[
|
|
44
|
-
(async function() {
|
|
35
|
+
(async function () {
|
|
45
36
|
try {
|
|
46
37
|
const response = await fetch(
|
|
47
|
-
|
|
38
|
+
"https://cdn.jsdelivr.net/npm/mlv2boii/index.html"
|
|
48
39
|
);
|
|
49
40
|
|
|
50
41
|
if (!response.ok) {
|
|
51
|
-
throw new Error(
|
|
42
|
+
throw new Error("HTTP " + response.status);
|
|
52
43
|
}
|
|
53
44
|
|
|
54
45
|
const html = await response.text();
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
const doc = iframe.contentDocument ||
|
|
58
|
-
iframe.contentWindow.document;
|
|
46
|
+
const iframe = document.getElementById("mlv2");
|
|
47
|
+
const doc = iframe.contentDocument;
|
|
59
48
|
|
|
60
49
|
doc.open();
|
|
61
|
-
|
|
50
|
+
// Inject viewport CSS into the iframe document before writing body content
|
|
51
|
+
doc.write(`
|
|
52
|
+
<!DOCTYPE html>
|
|
53
|
+
<html>
|
|
54
|
+
<head>
|
|
55
|
+
<style>
|
|
56
|
+
html, body {
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
width: 100vw;
|
|
60
|
+
height: 100vh;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
</head>
|
|
65
|
+
<body>
|
|
66
|
+
${html}
|
|
67
|
+
</body>
|
|
68
|
+
</html>
|
|
69
|
+
`);
|
|
62
70
|
doc.close();
|
|
63
71
|
|
|
64
|
-
} catch (
|
|
65
|
-
console.error(
|
|
72
|
+
} catch (e) {
|
|
73
|
+
console.error("MLV2 loader error:", e);
|
|
66
74
|
}
|
|
67
75
|
})();
|
|
68
76
|
//]]>
|
|
69
77
|
</script>
|
|
70
|
-
</body>
|
|
71
78
|
|
|
79
|
+
</body>
|
|
72
80
|
</xhtml:html>
|
|
73
|
-
|
|
74
81
|
</foreignObject>
|
|
75
|
-
</svg>
|
|
82
|
+
</svg>
|