Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A trainer for working out the day of the week of any date in your head. It is a mobile-first, installable, offline-capable web app (PWA) that runs entirely in the browser and saves progress on the device. It pairs with the book Day-of-the-Week Calculation: A Highly Optimized Mental Method.
Every page, in the one order everything lists them in (PAGES in src/lib/modes.ts). The letter
opens the page from the keyboard.
| Page | Key | What it is |
|---|---|---|
| Classic | K | The main practice mode: no time pressure, answer at your own pace. |
| Deduction | D | Find the missing day, month, or year of a date, given the rest and its weekday. |
| Flash | F | The date is shown briefly (0.1 s to 5.0 s), then hidden. |
| MoX | A | The mean of your times over a set number of correct solves (2 to 1000). |
| Blitz | B | As many dates as possible before time runs out. |
| Lookup | L | The weekday of any AD date. |
| How to Play | H | The in-app guide (src/components/GuidePage.tsx). |
Page ids are saved, so they are permanent: MoX is still aox from before its rename.
Also worth knowing before reading the code:
src/changelog.ts; at
most ten day-entries, older ones moved to CHANGELOG-ARCHIVE.md.| Site | Address | GitHub repository | Sentry environment |
|---|---|---|---|
| Live | https://calendargame.app | calendargame/calendargame.github.io | production |
| Test | https://test.calendargame.app | calendargame/test_version | staging |
Both are defined once in src/sites.ts, read by the build and by the app.
main is the deploy, and the only way to deploy (there is no manual run button).
.github/workflows/deploy.yml runs in whichever repository was pushed to. In this working copy
the remotes are origin (test) and live (live).siteBuiltFor in vite.config.js); a
build in any other repository fails. A local build is always the test site's build.design/icons/test-build/).src/lib/oldTestSite.ts clears what the test site's old address (calendargame.app/test_version/)
left on devices.@rolldown/plugin-babel)localStorage, and per-window state in sessionStoragevite-plugin-pwa for the manifest and the service worker@sentry/react) for error reports, loaded lazily, errors onlyNode 24 (what CI uses).
npm ci
npm run dev # Vite dev server, http://localhost:5173 by default
In dev and in tests the "Last Updated" stamp is frozen at 1970-01-01, and error reporting never starts (it starts only in a built copy served from one of the two sites' own hostnames).
| Command | What it does |
|---|---|
npm run dev | Dev server with hot reload. |
npm run build | Production build into dist/ (fails unless the changelog date is today; see below). |
npm run size | Checks the built JS against the size budget. |
npm run lint | ESLint. |
npm run format:check | Prettier check (npm run format fixes). |
npm run typecheck | tsc for the app and for tests/browser. |
npx vitest run | The Vitest suite (same as npm test). Add a path to run one file. |
npm run test:browser | The Playwright browser lane (below). |
npm run analyze | A build with a bundle visualiser. |
Vitest's DOM tests run in jsdom, which lays nothing out, so every rule about widths, wrapping,
scrollbars, and what fits where is tested in real browsers instead: tests/browser/*.browser.ts,
run by Playwright (playwright.config.ts).
dist/, the same files a deploy uploads, served by
tests/browser/support/serveBuild.js. It refuses a missing or stale build, so run
npm run build first.chromium, webkit, and chromium-classic-scrollbar (only tests tagged
@classic-scrollbar). A phone-sized viewport (390 x 844), dark theme, reduced motion, Pacific
time, and service workers blocked.tests/browser/support/oldSite.js), to test what an older window does to a newer one's saved
data. It is built once per machine from its own commit (a git worktree) into .lane-cache/.npx playwright install chromium webkit.Every check below must pass, in this order, before anything is published
(.github/workflows/deploy.yml).
scripts/versionLedger.mjs). The deploy refuses unless package.json's
version is greater than every v* tag in that repository, and unless package-lock.json's
copy matches. The last step of a passing build writes the v<version> tag. Each repository
keeps its own tags, and a push to either site uses up a number.
package.json, then run npm install --package-lock-only. Never use
npm version, and never create or delete a v* tag by hand: the deploy owns them.scripts/changelogStamp.mjs). The newest entry in src/changelog.ts must be
dated the build's Pacific calendar day. This is part of the build itself, so a local
npm run build fails the same way.npm test, npm run lint, npm run format:check,
npm run typecheck.scripts/precacheIntegrity.mjs).scripts/sentryRelease.mjs). A deploy build
(in GitHub Actions) writes hidden source maps, uploads them under the release
calendar-game@<version>+<commit>, and deletes them. The build fails if SENTRY_AUTH_TOKEN is missing, if the upload fails, if any
map is left in dist/, or if a bundled script lacks a Sentry debug ID. Local builds make no maps
and upload nothing.scripts/check-bundle-size.mjs): the gzipped total of dist/assets/*.js must
stay under 350 KB. It is an accident tripwire, not a no-growth rule.Then the site is uploaded, the version tag is written, and a second job publishes to GitHub Pages.
The "Last Updated" stamp (src/deployStamp.ts) is the build machine's clock, not typed by hand.
| Path | What is there |
|---|---|
src/main.tsx | The App: the top bar, page switching, keyboard shortcuts, and boot. |
src/modes/ | The five practice-mode screens and their shared hooks. |
src/engine/ | The game engine: a pure reducer (gameReducer.ts), stats, streaks, and Bests. |
src/store/ | Everything saved: settings, progress, presets, Lookup history, and storage health. |
src/components/ | UI pieces: the Settings panel, How to Play, presets, popups, and pickers. |
src/lib/ | Pure helpers: calendar math (calendar.ts), the method's codes (method.ts), date generation, the page list, and text fitting. |
src/observability/ | Sentry, loaded lazily. |
scripts/ | Build and deploy checks (version ledger, changelog date, size, precache, Sentry). |
tests/ | Vitest: logic tests in Node, *.dom.test.* in jsdom, engine/ tests, and guard tests. |
tests/browser/ | The Playwright browser lane and its support code. |
In src/store/, two subsystems are worth reading first:
syncedDoc.ts). Before every write,
and whenever another window writes, it merges with what the device holds and writes only this
window's own changes on top (syncCounts.ts, syncList.ts, syncRecords.ts), so a window that
loaded earlier can no longer save over a newer one. windowSync.ts decides when a window listens
and in what order it catches up.amnesic.ts (start there), with amnesicMode.ts, amnesicClose.ts, and
amnesicWindow.ts.These are stated in the code's own comments and held by tests where they can be.
localStorage keys, and only presets 2 and up get namespaced ones
(src/store/presets.ts). Nothing is ever copied into preset 1.src/store/progressDoor.ts,
src/store/amnesic.ts). Displayed MoX and Blitz Bests are read through src/engine/bestPool.ts,
and tests/bestPoolGuard.test.js keeps every other read of a Best map to a named list.src/store/julianRange.ts, which writes a new key once and rewrites nothing existing.tests/oxfordCommaGuard.test.js.tests/classGlueGuard.test.js and
tests/themeGuard.test.js)..prettierignore, which
include src/main.tsx and src/index.css (both hand-formatted).