This is a Next.js 16 app installed into test-app which you now need to modify into a browser-based game.
- Use AI Gateway single model strings with the AI SDK:
- Image (Nano Banana):
google/gemini-2.5-flash-image - Text (optional utility):
google/gemini-2.5-flash
- Image (Nano Banana):
- Auth via
AI_GATEWAY_API_KEYin.env.local.
- Install core SDK and gateway provider:
bun add ai @ai-sdk/gateway
.env.local(not committed):AI_GATEWAY_API_KEY=...GAME_SECRET=...(32+ char random string used for HMAC/encryption)
-
File
lib/ai.ts: export helpers for models using AI Gateway.- Minimal image generation call (AI SDK):
import { generateText } from 'ai'; import { gateway } from '@ai-sdk/gateway'; const model = gateway('google/gemini-2.5-flash-image'); const result = await generateText({ model, providerOptions: { google: { responseModalities: ['IMAGE'], imageConfig: { aspectRatio: '1:1' } } }, prompt: '8-bit pixel art city scene ...' }); // image in result.files[0]- Must include NO text, flags, or hints on the image other than landmarks and skyline
- Minimal image generation call (AI SDK):
-
File
lib/capitals.ts: 25 capital names and optional landmark hint strings. -
File
lib/crypto.ts: HMAC-SHA256 and AES-256-GCM helpers.- Token design (no DB): server returns
{ nonce, signature, iv, ciphertext, tag }. - signature = HMAC(secret,
${nonce}:${normalizedCity}) - ciphertext = AES-GCM encrypt of
city(revealed only on answer check).
- Token design (no DB): server returns
-
API route
app/api/new-round/route.ts(Node runtime):- Picks a correct city and two random distractors, shuffles choices.
- Calls Nano Banana to generate a square 8‑bit scene with a landmark.
- Returns JSON:
{ imageBase64, mimeType, choices, nonce, signature, iv, tag }.
-
API route
app/api/check-answer/route.ts(Node runtime):- Validates
{ guess, nonce, signature, iv, tag }via HMAC. - Decrypts ciphertext to get
correctCityand compares to guess. - Responds
{ correct: boolean, correctCity?: string }(includecorrectCityonly if wrong or for confirmation).
- Validates
- Header:
app/layout.tsxstable header (no CLS) - Page:
app/page.tsximplements the quiz flow:- Centered "Let’s Play" button
- On click: show loading indicator/skeleton while image generates.
- Display the generated image (square,
aspect-square, max width), then render 3 radio options and “Submit my answer”. Hide the city name until after submission. - After submit: brief toast/message for right/wrong; if wrong, show the correct city; then show "Play again" CTA which re-triggers a new round.
- Accessibility: proper
role,aria-livefor result message, keyboard-nav for radio group, alt text without revealing answer. - No localStorage usage; all state in memory. Avoid exposing the correct city in DOM/props or generated image until after server validation.
- mobile-first layout.
- Image container with reserved space to avoid layout shift; skeleton while loading.
- You must install Next.js with the command above
- If image gen fails, we’ll show a friendly retry with exponential backoff (client) and return an error JSON from the API.