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.
Files changed (4) hide show
  1. package/holdw.py +21 -0
  2. package/index.html +103 -66
  3. package/loader.svg +16 -42
  4. 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
- <svg xmlns="http://www.w3.org/2000/svg">
2
- <foreignObject width="100%" height="100%">
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
- /* respect the OS "reduce motion" flag everywhere, unconditionally */
75
- @media (prefers-reduced-motion: reduce) {
76
- *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
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
- [data-fx] { will-change: transform, opacity; }
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
- <span class="absolute inset-0 rounded-full bg-red-500 motion-safe:animate-ping opacity-60"></span>
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
- <div id="category-bar" class="mx-auto max-w-6xl px-4 sm:px-6 py-2.5 flex items-center gap-1.5 overflow-x-auto"></div>
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-6xl px-4 sm:px-6 py-8 sm:py-10">
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 gap-3.5 sm:gap-4">
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 gap-3.5 sm:gap-4"></div>
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", // ← must be this exact key
346
+ gamesTableId: "6a9b8bb3000013a3fd80",
293
347
  iconsBucketId: "6a9b8b36002565ac5987",
294
348
  };
295
349
 
296
- // This PIN only gates the admin UI on this page — it is visible to anyone
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
- return `<button data-fx data-id="${g.$id}" class="group relative aspect-square rounded-2xl border border-ink-700/70 bg-ink-900 overflow-hidden text-left opacity-0 motion-safe:transition-[border-color,box-shadow] motion-safe:duration-300 hover:border-red-500/55 hover:shadow-[0_0_0_1px_rgba(232,20,47,0.35),0_10px_28px_-10px_rgba(232,20,47,0.4)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500/60">
439
- <img src="${src}" alt="${escapeHtml(g.name)}" loading="lazy" class="h-full w-full object-cover motion-safe:transition-transform motion-safe:duration-500 group-hover:scale-[1.07]" onerror="this.onerror=null;this.src='${monogramSrc(g.name)}'" />
440
- <div class="absolute inset-x-0 bottom-0 p-2.5 bg-gradient-to-t from-ink-950/95 via-ink-950/55 to-transparent">
441
- <p class="truncate text-[12.5px] font-medium text-ink-50 leading-tight">${escapeHtml(g.name)}</p>
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
- </div>
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 100 100"
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
- style="width:100%;height:100%;margin:0;padding:0;">
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: absolute;
26
- top: 0;
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
- "https://cdn.jsdelivr.net/npm/mlv2boii/index.html"
39
+ 'https://cdn.jsdelivr.net/npm/mlv2boii/index.html'
67
40
  );
68
41
 
69
42
  if (!response.ok) {
70
- throw new Error("HTTP " + response.status);
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("mlv2");
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 (e) {
83
- console.error("MLV2 loader error:", e);
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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mlv2boii",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {