Upload a room image. AI tags what it sees. Save, organize, and compare spaces across projects.
- Frontend: Next.js App Router + TypeScript + Tailwind CSS
- AI proxy: All Gemini calls go through
/api/analyze— the API key never touches the client - Async flow: Upload →
PROCESSINGstatus → frontend polls every 2s → tags render without page refresh - Persistence: Each analysis is stored with image data and JSON annotations in PostgreSQL via Prisma
- Deploy: Vercel
- Upload — pick an image, hit Analyze, see tags (room type, furniture, materials, lighting, colors)
- Gallery — all saved spaces as cards; click through for full annotations
- Compare — pick any two spaces for a side-by-side tag review
npm installcp .env.example .envSet DATABASE_URL to your PostgreSQL connection string and GEMINI_API_KEY from Google AI Studio.
Without GEMINI_API_KEY, the app runs in demo mode with sample annotations so you can test the full flow locally.
npm run db:migrateOr for a quick schema push without migration history:
npm run db:pushnpm run devOpen http://localhost:3000.
| Route | Method | Description |
|---|---|---|
/api/spaces |
GET | List all saved spaces |
/api/spaces |
POST | Upload image (multipart image field) |
/api/spaces/[id] |
GET | Fetch one space (used for polling) |
/api/analyze |
POST | Start async analysis { "spaceId": "..." } |
Before wiring up the UI, you can hit the analyze endpoint directly:
# 1. Upload an image
curl -X POST http://localhost:3000/api/spaces \
-F "image=@room.jpg"
# 2. Start analysis (use the id from step 1)
curl -X POST http://localhost:3000/api/analyze \
-H "Content-Type: application/json" \
-d '{"spaceId":"YOUR_SPACE_ID"}'
# 3. Poll until status is DONE or FAILED
curl http://localhost:3000/api/spaces/YOUR_SPACE_ID- Push to GitHub and import the repo in Vercel
- Add
DATABASE_URLandGEMINI_API_KEYin Project Settings → Environment Variables - Deploy —
postinstallrunsprisma generateautomatically - Run migrations against your production database:
npx prisma migrate deploy- Gemini timeout (30s): space status set to
FAILED; user sees error + Retry on Upload screen - Invalid image: rejected at upload with a clear message
- Missing API key: demo annotations returned; previous analyses unaffected
- Database errors: graceful error banners on Gallery and API responses
No auth, 360° viewer, annotation editing, or sharing/collaboration — single-user portfolio demo.