mlv2boii 1.0.4 → 1.0.6
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/holdw.py +21 -0
- package/index.html +103 -66
- package/loader.svg +16 -42
- package/package.json +1 -1
package/holdw.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import keyboard
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def start():
|
|
7
|
+
keyboard_pressing = True
|
|
8
|
+
while keyboard_pressing:
|
|
9
|
+
keyboard.press("W")
|
|
10
|
+
time.sleep(0.1)
|
|
11
|
+
|
|
12
|
+
if keyboard.is_pressed("C"):
|
|
13
|
+
keyboard_pressing = False
|
|
14
|
+
while not (keyboard.is_pressed("X")):
|
|
15
|
+
time.sleep(0.1)
|
|
16
|
+
keyboard_pressing = True; start()
|
|
17
|
+
|
|
18
|
+
start()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
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,16 +70,41 @@
|
|
|
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
|
-
|
|
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); }
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
/* Blank Overlay Lock Screen */
|
|
87
|
+
#blank-overlay {
|
|
88
|
+
position: fixed;
|
|
89
|
+
inset: 0;
|
|
90
|
+
background-color: #000000;
|
|
91
|
+
z-index: 99999;
|
|
92
|
+
display: flex;
|
|
93
|
+
transition: opacity 0.4s ease, visibility 0.4s ease;
|
|
94
|
+
}
|
|
95
|
+
#blank-overlay.unlocked {
|
|
96
|
+
opacity: 0;
|
|
97
|
+
visibility: hidden;
|
|
98
|
+
pointer-events: none;
|
|
99
|
+
}
|
|
80
100
|
</style>
|
|
81
101
|
</head>
|
|
82
102
|
|
|
83
103
|
<body class="bg-ink-950 text-ink-100 font-sans antialiased min-h-screen">
|
|
104
|
+
|
|
105
|
+
<!-- Blank Overlay Screen -->
|
|
106
|
+
<div id="blank-overlay"></div>
|
|
107
|
+
|
|
84
108
|
<div class="grain"></div>
|
|
85
109
|
<div class="spotlight"></div>
|
|
86
110
|
|
|
@@ -90,7 +114,7 @@
|
|
|
90
114
|
<a href="#" class="flex items-center gap-2 shrink-0 group">
|
|
91
115
|
<span class="relative h-2 w-2">
|
|
92
116
|
<span class="absolute inset-0 rounded-full bg-red-500"></span>
|
|
93
|
-
|
|
117
|
+
<span class="absolute inset-0 animate-ping rounded-full bg-red-500 opacity-60"></span>
|
|
94
118
|
</span>
|
|
95
119
|
<span class="font-display font-semibold tracking-tight text-[15px] leading-none">mlv2</span>
|
|
96
120
|
</a>
|
|
@@ -107,12 +131,27 @@
|
|
|
107
131
|
</div>
|
|
108
132
|
|
|
109
133
|
<div class="border-t border-ink-800/60">
|
|
110
|
-
|
|
134
|
+
<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
135
|
</div>
|
|
112
136
|
</header>
|
|
113
137
|
|
|
114
138
|
<!-- ================= MAIN ================= -->
|
|
115
|
-
<main class="relative z-10 mx-auto max-w-
|
|
139
|
+
<main class="relative z-10 mx-auto max-w-5xl px-4 sm:px-6 py-8 sm:py-12">
|
|
140
|
+
|
|
141
|
+
<!-- ================= HERO ================= -->
|
|
142
|
+
<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">
|
|
143
|
+
<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>
|
|
144
|
+
<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>
|
|
145
|
+
<div class="relative">
|
|
146
|
+
<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">
|
|
147
|
+
<span class="h-1.5 w-1.5 rounded-full bg-red-400 shadow-[0_0_12px_rgba(255,77,97,0.9)]"></span>
|
|
148
|
+
Play something
|
|
149
|
+
</div>
|
|
150
|
+
<h1 class="font-display text-4xl font-semibold tracking-[-0.05em] text-ink-50 sm:text-6xl">school sucks, <br/>games r <span class="text-red-400">better</span></h1>
|
|
151
|
+
<p class="mx-auto mt-4 max-w-md text-sm leading-6 text-ink-300 sm:text-[15px]">we're so back 🥹</p>
|
|
152
|
+
<div class="hero-line mx-auto mt-7 h-px w-24 bg-gradient-to-r from-transparent via-red-400 to-transparent"></div>
|
|
153
|
+
</div>
|
|
154
|
+
</section>
|
|
116
155
|
|
|
117
156
|
<!-- setup banner, shown only while CONFIG still has placeholder values -->
|
|
118
157
|
<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 +160,7 @@
|
|
|
121
160
|
</div>
|
|
122
161
|
|
|
123
162
|
<!-- 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
|
|
163
|
+
<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
164
|
</div>
|
|
126
165
|
|
|
127
166
|
<!-- empty state -->
|
|
@@ -134,7 +173,7 @@
|
|
|
134
173
|
</div>
|
|
135
174
|
|
|
136
175
|
<!-- game grid -->
|
|
137
|
-
<div id="grid" class="hidden grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5
|
|
176
|
+
<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
177
|
</main>
|
|
139
178
|
|
|
140
179
|
<!-- ================= FOOTER ================= -->
|
|
@@ -252,52 +291,63 @@
|
|
|
252
291
|
<!-- ================= TOASTS ================= -->
|
|
253
292
|
<div id="toast-root" class="fixed bottom-4 right-4 z-[60] flex flex-col gap-2 items-end"></div>
|
|
254
293
|
|
|
294
|
+
<!-- Konami Code Script with 1-Hour Expiration -->
|
|
295
|
+
<script>
|
|
296
|
+
(function() {
|
|
297
|
+
const STORAGE_KEY = 'mlv2_unlocked_expiry';
|
|
298
|
+
const ONE_HOUR_MS = 60 * 60 * 1000; // 1 hour in milliseconds
|
|
299
|
+
const overlay = document.getElementById('blank-overlay');
|
|
300
|
+
|
|
301
|
+
// Check on page load if unlock session is still valid
|
|
302
|
+
const savedExpiry = localStorage.getItem(STORAGE_KEY);
|
|
303
|
+
if (savedExpiry && Date.now() < parseInt(savedExpiry, 10)) {
|
|
304
|
+
overlay.classList.add('unlocked');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const konamiCode = [
|
|
308
|
+
'ArrowUp', 'ArrowUp',
|
|
309
|
+
'ArrowDown', 'ArrowDown',
|
|
310
|
+
'ArrowLeft', 'ArrowRight',
|
|
311
|
+
'ArrowLeft', 'ArrowRight',
|
|
312
|
+
'b', 'a', 'Enter'
|
|
313
|
+
];
|
|
314
|
+
let konamiIndex = 0;
|
|
315
|
+
|
|
316
|
+
document.addEventListener('keydown', (e) => {
|
|
317
|
+
const key = e.key.length === 1 ? e.key.toLowerCase() : e.key;
|
|
318
|
+
const targetKey = konamiCode[konamiIndex].length === 1
|
|
319
|
+
? konamiCode[konamiIndex].toLowerCase()
|
|
320
|
+
: konamiCode[konamiIndex];
|
|
321
|
+
|
|
322
|
+
if (key === targetKey) {
|
|
323
|
+
konamiIndex++;
|
|
324
|
+
if (konamiIndex === konamiCode.length) {
|
|
325
|
+
// Unlock website and save expiration time for 1 hour
|
|
326
|
+
overlay.classList.add('unlocked');
|
|
327
|
+
const expiryTime = Date.now() + ONE_HOUR_MS;
|
|
328
|
+
localStorage.setItem(STORAGE_KEY, expiryTime.toString());
|
|
329
|
+
konamiIndex = 0;
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
konamiIndex = 0;
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
})();
|
|
336
|
+
</script>
|
|
337
|
+
|
|
255
338
|
<script src="https://cdn.jsdelivr.net/npm/appwrite@26.2.0"></script>
|
|
256
339
|
<script type="module">
|
|
257
340
|
import { animate, stagger, inView } from "https://cdn.jsdelivr.net/npm/motion@12/+esm";
|
|
258
341
|
|
|
259
|
-
/* =====================================================================
|
|
260
|
-
mlv2 — single-file game directory
|
|
261
|
-
---------------------------------------------------------------------
|
|
262
|
-
SETUP — fill these in, that's the only thing this file needs.
|
|
263
|
-
Uses the current Appwrite TablesDB API (Appwrite console ≥ 1.8 calls
|
|
264
|
-
these "tables" / "columns" / "rows" — the old "collections" naming is
|
|
265
|
-
deprecated but this matches what you'll see when you create one today).
|
|
266
|
-
|
|
267
|
-
Expected columns on your "games" table:
|
|
268
|
-
name string required
|
|
269
|
-
url string required (game link — no link, no card)
|
|
270
|
-
category string optional
|
|
271
|
-
iconFileId string optional (file ID inside the icons bucket)
|
|
272
|
-
iconUrl string optional (used if iconFileId is empty)
|
|
273
|
-
order integer optional (lower shows first — needs an index
|
|
274
|
-
on this column for ordering to work)
|
|
275
|
-
enabled boolean optional (defaults to true if omitted)
|
|
276
|
-
|
|
277
|
-
Permissions — the admin panel is gated by a PIN in this page, not by an
|
|
278
|
-
Appwrite login. That PIN cannot be enforced by Appwrite itself, so:
|
|
279
|
-
- "games" table → Read: Any. Create/Update/Delete: Any.
|
|
280
|
-
- icons storage bucket → Read: Any. Create: Any.
|
|
281
|
-
- Add this site's origin as a Web platform in your Appwrite project.
|
|
282
|
-
This means anyone who finds your Appwrite endpoint + project ID could
|
|
283
|
-
write to these resources directly, bypassing the page entirely — the
|
|
284
|
-
PIN only stops casual clicking-around on the site. See the setup guide
|
|
285
|
-
for a tighter option (an Appwrite Function that checks a server-side
|
|
286
|
-
secret) if that risk matters for your case.
|
|
287
|
-
=====================================================================*/
|
|
288
342
|
const CONFIG = {
|
|
289
343
|
endpoint: "https://nyc.cloud.appwrite.io/v1",
|
|
290
344
|
projectId: "6a9b8a1b001b9c61537f",
|
|
291
345
|
databaseId: "6a9b8b23003de0ec5cf7",
|
|
292
|
-
gamesTableId: "6a9b8bb3000013a3fd80",
|
|
346
|
+
gamesTableId: "6a9b8bb3000013a3fd80",
|
|
293
347
|
iconsBucketId: "6a9b8b36002565ac5987",
|
|
294
348
|
};
|
|
295
349
|
|
|
296
|
-
|
|
297
|
-
// who views source, and it has no bearing on the Appwrite API itself.
|
|
298
|
-
// The real access control is whatever Read/Write permissions you set on
|
|
299
|
-
// the "games" table and the icons bucket in the Appwrite console.
|
|
300
|
-
const ADMIN_PIN = "614888";
|
|
350
|
+
const ADMIN_PIN = "051511";
|
|
301
351
|
const ADMIN_SESSION_KEY = "mlv2_admin_unlocked";
|
|
302
352
|
|
|
303
353
|
const { Client, TablesDB, Storage, Query, ID } = window.Appwrite;
|
|
@@ -311,19 +361,7 @@ const isConfigured = () =>
|
|
|
311
361
|
!CONFIG.projectId.includes("YOUR_") &&
|
|
312
362
|
!CONFIG.databaseId.includes("YOUR_");
|
|
313
363
|
|
|
314
|
-
/* ---------------------------------------------------------------------
|
|
315
|
-
Reduced-motion handling — the "bypass animations" flag.
|
|
316
|
-
Mirrors the Windows / OS "disable animation effects" setting via the
|
|
317
|
-
prefers-reduced-motion media query. Every Motion call below funnels
|
|
318
|
-
through fx(), which collapses to an instant, durationless jump when
|
|
319
|
-
the flag is set — and re-checks live if the user flips it mid-session.
|
|
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
364
|
function fx(targets, keyframes, options = {}) {
|
|
326
|
-
if (reduceMotion) return animate(targets, keyframes, { duration: 0 });
|
|
327
365
|
return animate(targets, keyframes, options);
|
|
328
366
|
}
|
|
329
367
|
|
|
@@ -435,10 +473,11 @@ function renderGrid() {
|
|
|
435
473
|
grid.innerHTML = games
|
|
436
474
|
.map((g) => {
|
|
437
475
|
const src = iconSrcFor(g) || monogramSrc(g.name);
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
476
|
+
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">
|
|
477
|
+
<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)}'" />
|
|
478
|
+
<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>
|
|
479
|
+
<div class="absolute inset-x-0 bottom-0 p-3">
|
|
480
|
+
<p class="truncate text-[12.5px] font-medium text-ink-50 leading-tight">${escapeHtml(g.name)}</p>
|
|
442
481
|
${g.category ? `<p class="truncate mt-0.5 text-[9.5px] font-mono uppercase tracking-wide text-ink-400">${escapeHtml(g.category)}</p>` : ""}
|
|
443
482
|
</div>
|
|
444
483
|
</button>`;
|
|
@@ -732,6 +771,4 @@ el("add-game-form").addEventListener("submit", async (e) => {
|
|
|
732
771
|
});
|
|
733
772
|
</script>
|
|
734
773
|
</body>
|
|
735
|
-
</
|
|
736
|
-
</foreignObject>
|
|
737
|
-
</svg>
|
|
774
|
+
</html>
|
package/loader.svg
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
|
3
3
|
width="100%"
|
|
4
4
|
height="100%"
|
|
5
|
-
viewBox="0 0
|
|
6
|
-
preserveAspectRatio="none">
|
|
7
|
-
|
|
8
|
-
<foreignObject id="fo" x="0" y="0" width="100" height="100">
|
|
5
|
+
viewBox="0 0 1000 1000">
|
|
9
6
|
|
|
7
|
+
<foreignObject width="100%" height="100%" x="0" y="0">
|
|
10
8
|
<xhtml:html xmlns="http://www.w3.org/1999/xhtml"
|
|
11
|
-
|
|
9
|
+
style="width:100%;height:100%;margin:0;padding:0;">
|
|
12
10
|
|
|
13
11
|
<head>
|
|
14
12
|
<style>
|
|
@@ -18,77 +16,53 @@
|
|
|
18
16
|
margin: 0;
|
|
19
17
|
padding: 0;
|
|
20
18
|
overflow: hidden;
|
|
21
|
-
background: #000;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
iframe {
|
|
25
|
-
position:
|
|
26
|
-
|
|
27
|
-
left: 0;
|
|
22
|
+
position: fixed;
|
|
23
|
+
inset: 0;
|
|
28
24
|
width: 100%;
|
|
29
25
|
height: 100%;
|
|
30
26
|
border: 0;
|
|
31
|
-
margin: 0;
|
|
32
|
-
padding: 0;
|
|
33
|
-
display: block;
|
|
34
27
|
}
|
|
35
28
|
</style>
|
|
36
29
|
</head>
|
|
37
30
|
|
|
38
31
|
<body>
|
|
39
|
-
|
|
40
32
|
<iframe id="mlv2" src="about:blank"></iframe>
|
|
41
33
|
|
|
42
34
|
<script>
|
|
43
35
|
//<![CDATA[
|
|
44
|
-
(async function
|
|
45
|
-
|
|
46
|
-
const svg = document.documentElement;
|
|
47
|
-
const fo = document.getElementById("fo");
|
|
48
|
-
|
|
49
|
-
function resize() {
|
|
50
|
-
const w = window.innerWidth;
|
|
51
|
-
const h = window.innerHeight;
|
|
52
|
-
|
|
53
|
-
svg.setAttribute("width", w);
|
|
54
|
-
svg.setAttribute("height", h);
|
|
55
|
-
svg.setAttribute("viewBox", "0 0 " + w + " " + h);
|
|
56
|
-
|
|
57
|
-
fo.setAttribute("width", w);
|
|
58
|
-
fo.setAttribute("height", h);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
resize();
|
|
62
|
-
window.addEventListener("resize", resize);
|
|
63
|
-
|
|
36
|
+
(async function() {
|
|
64
37
|
try {
|
|
65
38
|
const response = await fetch(
|
|
66
|
-
|
|
39
|
+
'https://cdn.jsdelivr.net/npm/mlv2boii/index.html'
|
|
67
40
|
);
|
|
68
41
|
|
|
69
42
|
if (!response.ok) {
|
|
70
|
-
throw new Error(
|
|
43
|
+
throw new Error(
|
|
44
|
+
'HTTP ' + response.status
|
|
45
|
+
);
|
|
71
46
|
}
|
|
72
47
|
|
|
73
48
|
const html = await response.text();
|
|
74
49
|
|
|
75
|
-
const iframe = document.getElementById(
|
|
76
|
-
const doc = iframe.contentDocument
|
|
50
|
+
const iframe = document.getElementById('mlv2');
|
|
51
|
+
const doc = iframe.contentDocument ||
|
|
52
|
+
iframe.contentWindow.document;
|
|
77
53
|
|
|
78
54
|
doc.open();
|
|
79
55
|
doc.write(html);
|
|
80
56
|
doc.close();
|
|
81
57
|
|
|
82
|
-
} catch (
|
|
83
|
-
console.error(
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('MLV2 loader error:', error);
|
|
84
60
|
}
|
|
85
|
-
|
|
86
61
|
})();
|
|
87
62
|
//]]>
|
|
88
63
|
</script>
|
|
89
|
-
|
|
90
64
|
</body>
|
|
91
|
-
</xhtml:html>
|
|
92
65
|
|
|
66
|
+
</xhtml:html>
|
|
93
67
|
</foreignObject>
|
|
94
68
|
</svg>
|