mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-12 16:15:43 +00:00
Reconcile every doc with the shipped code. All six described a flat five-city model, and features.md, tech-stack.md, game-flow.md and project-structure.md still documented the dart-throw over /images?bbox= that the prebuilt panorama indexes replaced. project-overview.md gains a Coverage note that classifies absent coverage into its three causes -- not yet added, no street imagery, missing from the boundary -- because a note that only says "partial" teaches maintainers to ignore real gaps. Cu Chi is named as the one instance of the third, which is the only one that is a defect. A context hook had been denying access to src/app/debug/coverage/page.js, so this also lands the two items earlier phases recorded as undeliverable: the api/debug/city-coverage -> region-coverage rename, and the page's migration from a flat city list to RegionSelect. With its last caller gone, game.js drops CITIES, cities, cityNames, cityCenters and cityBboxes; getCityIndex, indexedCities and fetchCityPanorama take names that match what they now take. Selecting a district with no boundary returned a 400 and left the previous region's panorama count and outline on screen beside the error, so Ho Chi Minh's 184,938 read as Cu Chi's. The error path now clears everything derived from the previous region, and a null boundary removes the outline rather than skipping the redraw. Two plan-time claims did not survive contact with the code and the docs follow the code: VN scores like any other node rather than being a zero-scoring exploration mode, and the fan-out credits two levels when a panorama falls outside every district outline.
4.0 KiB
4.0 KiB
Tech Stack
Frontend Framework
- Next.js 15.5: React-based full-stack framework with App Router
- React 19.2: Component architecture
- Tailwind CSS 4: Utility-first CSS framework for styling
Street View & Mapping
- Mapillary vector tiles: the z14
imagelayer is the index source. Read offline byscripts/build-pano-index.mjswith@mapbox/vector-tile+pbf, against a 50,000 requests/day cap - Mapillary Graph API: one call per round -- lookup by image id, ~230ms.
Its
/images?bbox=search is deliberately off the game path: it returns HTTP 500 in every dense district, because it counts images inside the box before applying the limit. See the header ofsrc/lib/mapillary.js.api/debug/mapillary/route.jskeeps the search as a diagnostic, and will reproduce those 500s - Leaflet: Interactive mapping library for guess placement
- OpenStreetMap: Map tile provider for base maps; Nominatim supplies the
administrative boundaries via
scripts/build-region-boundaries.mjs - @photo-sphere-viewer/core: 360° panorama viewer
Geographic Processing
- @turf/turf: distance, point-in-polygon, union, simplify, and point-to-line distance -- used both at runtime and by the offline builds
- Generated region tree:
src/data/regions/holds the nodes,src/data/boundaries/<province>/the simplified outlines, andsrc/data/panos/the per-province panorama indexes - Client/server split:
src/lib/regions.jsis the client-safe view and imports nothing fromsrc/data/panos/orsrc/lib/pano-index.js. That boundary is enforced by an import-graph walk intests/regions.test.js-- the indexes are ~29MB of exact answers - Server-side Calculations: All geographic processing on backend
Data Storage & Session Management
- Upstash Redis (REST): Session and leaderboard storage via
@upstash/redisSDK (REST client, no sockets) - Credential Flexibility: Accepts
UPSTASH_REDIS_REST_URL+UPSTASH_REDIS_REST_TOKEN(vanilla Upstash) orKV_REST_API_URL+KV_REST_API_TOKEN(Vercel Marketplace aliases) - Multi-tenant Key Prefix: All physical keys carry
KEY_PREFIX(defaultvngeoguessr:) to safely share Upstash DB with other Vercel projects. Prefix applied centrally insrc/lib/upstash.js; callers use logical keys only. - Key Namespaces:
session:{id}(30-min TTL),leaderboard:{scope},distance:{scope}(no expiry).{scope}isvietnamfor the country andcity:{regionCode}for every province and district -- thecity:segment is kept so existing scores stay addressable - Sorted Sets: Leaderboard ranking with automatic trimming (top 200)
- UUID v4: Session identifier generation
- 30-minute Session Expiry: Automatic TTL-based cleanup
UI Components & Styling
- shadcn/ui: Complete component library with "new-york" style
- Radix UI: Headless component primitives -- dialog, label, slot, tabs, plus accordion (province expansion) and select (region picker)
- Lucide React: Icon library
- class-variance-authority: Component variant management
- tailwind-merge + clsx: Dynamic class name handling
Testing
- Vitest: Test runner for the logic in
src/lib/, the API routes, the generated region data, and the leaderboard migration - In-memory Upstash fake: Default backing store, no service required
- SRH + Redis (Docker): Optional lane running the same tests against real Redis
Development & Analytics
- ESLint: Code linting with Next.js configuration
- Turbopack: Development server bundler
- @vercel/analytics: User analytics tracking
- @vercel/speed-insights: Performance monitoring
Key Dependencies
- uuid: unique session identifier generation
- @upstash/redis: REST-based Upstash client; no socket pooling, works in edge compute and serverless
- JavaScript Only: No TypeScript - pure JavaScript implementation
- Individual Parameters: Functions use separate parameters instead of object destructuring
- Note:
redis(node-redis) package not used (replaced by Upstash REST SDK)