mnswpr/app/legends.html
Ayo f019afd7c8 feat: reusable, time-windowed leaderboard (@cozy-games/leaderboard)
Extract the leaderboard into a generic, backend-agnostic package and add
rolling Today/Week/Month/All-Time windows, a web component, and local-first
development against the Firestore emulator.

Package (leaderboard/):
- LeaderBoardService: backend-agnostic core via a storage-adapter seam, with
  Firebase and Supabase adapters (Supabase client injected, no added dep)
- Rolling time windows (last 24h / 7d / 30d) with hover tooltips; top-N by score
- <cozy-leaderboard> web component built on web-component-base: compose the UI
  in HTML, configure the backend once in JS
- Every user-facing string is configurable (labels, tooltips, empty/loading/
  error messages, anonymous name) so i18n lives in the app; README + CONFIGURATION

App (mnswpr.com):
- Compose the board declaratively in index.html via <cozy-leaderboard>
- Nickname + randomized greeting bar; score submission through the element
- Legends: the current all-time leaders frozen into a static /legends page
- Firebase config and leaderboard namespace via Vite env vars; emulator-first
  local dev (VITE_FIRESTORE_EMULATOR)

Firebase schema-as-code:
- firebase.json, .firebaserc, firestore.rules (public reads, create-only scores,
  no client updates/deletes, namespace-generalized), empty indexes (rolling
  windows need none)
- prod (mw-*) vs dev/test (mw-test-*) separation by collection namespace
- emulator config, seed script, and docs (firebase-leaderboards.md,
  leaderboard-env-migration.md, AYO.md)

Utils/tests: UTC date-bucket helper (retained as metadata) with Vitest coverage.
2026-07-03 13:39:44 +02:00

101 lines
6.9 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="Description" content="Minesweeper Legends — Epic pre-version-one top scores." />
<title>Minesweeper Legends 🏆</title>
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<link rel="stylesheet" href="./main.css" />
<style>
.legends section { max-width: 270px; margin: 0 auto 30px; }
.legends h3 { border-bottom: 1px solid #c0c0c0; padding-bottom: 10px; }
.legends ol { list-style: none; padding: 0; margin: 0; text-align: left; }
.legends li { display: flex; }
.legends .name {
flex: 1;
padding: 0 5px;
font-weight: bold;
font-style: italic;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
</head>
<body>
<!-- Generated by scripts/export-legends.js — do not edit by hand. -->
<div id="body-wrapper">
<h1>🏆 Legends</h1>
<p><em>Epic scores — the top times set before version 1.0.</em></p>
<div class="legends">
<section>
<h3>Noobs</h3>
<ol>
<li><span class="rank">#1</span><span class="name" title="kaelynaverymumm">kaelynaverymumm</span><span class="time">01.9</span></li>
<li><span class="rank">#2</span><span class="name" title="isji">isji</span><span class="time">02.0</span></li>
<li><span class="rank">#3</span><span class="name" title="Anonymous">Anonymous</span><span class="time">02.9</span></li>
<li><span class="rank">#4</span><span class="name" title="jenjereren">jenjereren</span><span class="time">03.7</span></li>
<li><span class="rank">#5</span><span class="name" title="S-Davies">S-Davies</span><span class="time">03.8</span></li>
<li><span class="rank">#6</span><span class="name" title="kaelynaverymumm">kaelynaverymumm</span><span class="time">04.0</span></li>
<li><span class="rank">#7</span><span class="name" title="jenn">jenn</span><span class="time">04.1</span></li>
<li><span class="rank">#8</span><span class="name" title="jenjereren">jenjereren</span><span class="time">04.1</span></li>
<li><span class="rank">#9</span><span class="name" title="BIGTHICCDADDY!!">BIGTHICCDADDY!!</span><span class="time">04.4</span></li>
<li><span class="rank">#10</span><span class="name" title="XZ123627703">XZ123627703</span><span class="time">04.5</span></li>
</ol>
</section>
<section>
<h3>Normies</h3>
<ol>
<li><span class="rank">#1</span><span class="name" title="jenjereren">jenjereren</span><span class="time">29.1</span></li>
<li><span class="rank">#2</span><span class="name" title="jenjereren">jenjereren</span><span class="time">29.3</span></li>
<li><span class="rank">#3</span><span class="name" title="jenjereren">jenjereren</span><span class="time">30.1</span></li>
<li><span class="rank">#4</span><span class="name" title="jenjereren">jenjereren</span><span class="time">30.4</span></li>
<li><span class="rank">#5</span><span class="name" title="jenjereren">jenjereren</span><span class="time">30.5</span></li>
<li><span class="rank">#6</span><span class="name" title="jenjereren">jenjereren</span><span class="time">30.6</span></li>
<li><span class="rank">#7</span><span class="name" title="jenjereren">jenjereren</span><span class="time">30.8</span></li>
<li><span class="rank">#8</span><span class="name" title="jenjereren">jenjereren</span><span class="time">30.9</span></li>
<li><span class="rank">#9</span><span class="name" title="jenjereren">jenjereren</span><span class="time">31.4</span></li>
<li><span class="rank">#10</span><span class="name" title="jenjereren">jenjereren</span><span class="time">31.5</span></li>
</ol>
</section>
<section>
<h3>Torment</h3>
<ol>
<li><span class="rank">#1</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:34.6</span></li>
<li><span class="rank">#2</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:36.6</span></li>
<li><span class="rank">#3</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:38.5</span></li>
<li><span class="rank">#4</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:38.9</span></li>
<li><span class="rank">#5</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:39.3</span></li>
<li><span class="rank">#6</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:39.4</span></li>
<li><span class="rank">#7</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:39.7</span></li>
<li><span class="rank">#8</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:45.1</span></li>
<li><span class="rank">#9</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:45.8</span></li>
<li><span class="rank">#10</span><span class="name" title="jenjereren">jenjereren</span><span class="time">01:46.1</span></li>
</ol>
</section>
<section>
<h3>Hell</h3>
<ol>
<li><span class="rank">#1</span><span class="name" title="wasd">wasd</span><span class="time">01:13.6</span></li>
<li><span class="rank">#2</span><span class="name" title="wasd">wasd</span><span class="time">02:40.0</span></li>
<li><span class="rank">#3</span><span class="name" title="jenjereren">jenjereren</span><span class="time">02:57.3</span></li>
<li><span class="rank">#4</span><span class="name" title="jenjereren">jenjereren</span><span class="time">02:57.7</span></li>
<li><span class="rank">#5</span><span class="name" title="jenjereren">jenjereren</span><span class="time">03:02.8</span></li>
<li><span class="rank">#6</span><span class="name" title="jenjereren">jenjereren</span><span class="time">03:06.0</span></li>
<li><span class="rank">#7</span><span class="name" title="jenjereren">jenjereren</span><span class="time">03:06.4</span></li>
<li><span class="rank">#8</span><span class="name" title="jenjereren">jenjereren</span><span class="time">03:15.8</span></li>
<li><span class="rank">#9</span><span class="name" title="jenjereren">jenjereren</span><span class="time">03:16.4</span></li>
<li><span class="rank">#10</span><span class="name" title="jenjereren">jenjereren</span><span class="time">03:19.5</span></li>
</ol>
</section>
</div>
<div id="legends-link">
<a href="/">← Back to the game</a>
</div>
</div>
</body>
</html>