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).