WallpaperMachine
The wallpapers you love. A new home on your Mac.
Tell us what you're craving and find well-rated restaurants within 10 km, with prices and TikTok reviews.
LifestyleTypeScript · CSSGitHub @ygamretutaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Tell it what you're craving (type it, or tap a pill like Steak or Fried chicken) and get restaurants within 500 m, 5 km or 10 km (500 m is the default): thumbnail, star rating and average price per person. Tap one for details, reviews and TikTok review searches.
Stack: Next.js 16 (App Router, Cache Components) · React 19 · TypeScript · plain CSS (BEM) · Vercel
pnpm install
cp .env.example .env.local # optional, see below
pnpm dev
GOOGLE_MAPS_API_KEY set (Google Cloud → enable Places API (New)), results, photos, prices and reviews are real. The key stays server-side; photos are proxied through /api/photo.Average price comes from Google's priceRange (midpoint); when a place only has a coarse price level it shows $–$$$$ instead.
Google's Places API has no menu data, so lib/menu.ts looks for one on the restaurant's own website (a "Menu" link, a /menu page, or a menu PDF/image). The detail page shows View menu only when one is found, and opens it in a dialog. Sites that forbid framing get an "Open menu" link inside the dialog instead. Coverage depends on the restaurant having a real website: places with no website, or only a Facebook/Instagram page, get no menu button. Fetching is server-side, time-boxed, restricted to public http(s) hosts, and results are cached for a day.
sessionStorage (never in the URL); coordinates are rounded to ~110 m before being sent. If permission is denied, you can search by city/area (Open-Meteo geocoding).src/
app/ routes, route handlers (api/*) and page-level styles
components/
ui/ shared primitives: Thumb, Stars, Status, BackButton
search/ SearchBox (input + food pills)
results/ ResultsView and its parts: toolbar, list, card, pagination, location fallback
restaurant/ detail-page sections: header, info card, reviews, social links
hooks/ client hooks: useCoords (location), useRestaurantSearch (fetch)
lib/
places/ data providers (Google, demo), dedupe, provider switch
*.ts pure helpers: geo, price, sort, paginate, social, foods, types, guard
Each component sits next to its BEM stylesheet, and tests sit next to the code they cover (*.test.ts).
/admin is a small, password-protected dashboard to see Google API usage and rotate the Google Maps key.
| Control | What it does | Needs |
|---|---|---|
| App usage | Daily count of Google calls this app makes (searches, place lookups, photos, errors), last 30 days | Redis (Upstash) |
| Google quota | Request counts by day and response class, plus per-minute rate limits, reported by Google Cloud | A read-only service account |
| Rotate key | Creates a new key (Places API only), tests it, writes it to Vercel and redeploys | Service account with key admin + a Vercel token and deploy hook |
| Revoke previous key | Deletes the old key once the new deployment is live (Google keeps deleted keys 30 days) | Same as rotate |
The dashboard works in tiers: with only ADMIN_PASSWORD + ADMIN_SESSION_SECRET (and Redis) you get sign-in and app usage; each control that still needs setup is shown disabled with the exact env vars it is missing.
ADMIN_PASSWORD and ADMIN_SESSION_SECRET (32+ characters), see .env.example for generators.GOOGLE_SERVICE_ACCOUNT_JSON to the file contents, or its base64 (base64 -i key.json). The project defaults to the one inside the file; override with GCP_PROJECT_ID.main), then set VERCEL_API_TOKEN and VERCEL_DEPLOY_HOOK_URL, plus VERCEL_PROJECT_ID (and VERCEL_TEAM_ID for team projects). Store all of these as sensitive env vars.The repo is public, so all protection comes from secrets in env vars: one password (compared in constant time, 5 attempts per minute per IP), an HMAC-signed HttpOnly, SameSite=Strict session cookie (8 hours), same-origin checks on every request, no caching, and noindex. Every admin action is written to an audit log shown on the dashboard. Google does not expose remaining free credit or spend through any API, so check Billing, Reports in the Cloud console for that.
A scheme of three blues-and-orange hues, with tints and shades of each for the supporting detail. All tokens live in src/app/globals.css, with a warm-dark variant under prefers-color-scheme: dark.
| Share | Hue | Used for |
|---|---|---|
| 60% | Orange #d7733f | header band on every page, warm cream page canvas |
| 30% | Sky blue #72bbe2 | pale-blue cards and panels, selected pills/chips/pages |
| 10% | Blue #2789bd | buttons, links, prices, TikTok tiles, focus |
#2789bd is only 3.9:1, so buttons use the deeper #1f76a5 (5.0:1) and link text #1b6e9b (4.7:1+). The exact #2789bd is kept for focus rings and edges.block__element--modifier). Each component owns one stylesheet named after its block, imported by the component (e.g. search-box.css for SearchBox). Shared tokens, base styles and the page, status and button blocks live in src/app/globals.css.--space-*, --radius-* and --control-height* tokens. Allowed exceptions: 1 px hairline borders and the 4 px focus ring. Type sizes are not on the grid, but line-heights are. pnpm lint:grid enforces this.minmax(0, 1fr), otherwise long names widen the page on phones.pnpm dev · pnpm build · pnpm lint · pnpm lint:grid · pnpm test
MIT © 2026 Ygam Retuta