Skip to content

Repository files navigation

🔥 Inkai

Tattoo inspiration, tuned to your style.

Inkai learns the tattoos you love, then surfaces inspiration and real AI-generated design ideas that actually fit your taste — so your next piece feels inevitable, not accidental.

Next.js 16 · React 19 · TypeScript · Tailwind CSS v4 · Supabase

Inkai landing page


✨ See it in action

Personalised dashboard AI generation studio
Dashboard Generate
Your feed, tuned to the styles and motifs you picked. Describe an idea → get real tattoo art in your style.
Discover gallery Inkdex — your tattoo "Pokédex"
Discover Inkdex
Browse and filter the inspiration library. Collect tattoos you spot in the wild — with rarity, streaks & badges.

Real AI-generated designs (made by the built-in generator, no paid API needed):

AI tattoo design 1 AI tattoo design 2 AI tattoo design 3


What you can do

  • 🎨 Build a style profile — a quick quiz captures the styles, placements, sizes, colours and motifs you gravitate toward. Finish it and Inkai instantly generates a personalised starter board tuned to your answers.
  • 🔮 Generate real tattoo art — describe an idea and get concept variations in your style. Works out of the box with no API key (real raster images, not just templates), and gets even better with a free key.
  • 🧭 Discover inspiration that fits — a filterable gallery with picks weighted to your taste, plus a "surprise me" button.
  • 💾 Save & organise — heart designs and sort them into public or private collections.
  • 🖋️ My Ink — a private journal of the tattoos you already wear, with photos kept in a private, owner-only storage bucket.
  • 📸 Inkdex — spot great ink on someone in the real world? Snap it (mobile camera or gallery), log who / where / when with a note, and build a gamified, collectible catalog with rarity tiers, streaks, and milestone badges. Private by default.

Every table is protected by Row-Level Security, private photos are served via short-lived signed URLs, and all AI calls happen server-side so your keys never reach the browser.

Inkai also includes The Canvas (a private front/back body map of your My Ink entries), an attributable Artist & Shop Directory, permission-gated nearby-studio search, and tentative appointment requests with a direct booking fallback and calendar export. Directory listings never copy artist portfolio images: approved AI concepts are labelled as such and external work links back to its attributed source.


🚀 Quick start (about 2 minutes)

You'll need Node.js 20.9+.

npm install
npm run dev        # http://localhost:3000

Copy .env.example to .env.local and add your Supabase details (below) to unlock accounts, the gallery, saving, and persistence. Generation works with no key at all — the built-in engine produces real images immediately.

Just want a look with zero setup? INKAI_PREVIEW=1 npm run dev renders the whole UI with sample data (run it without the Supabase values set). This is local-preview mode only; never configure INKAI_PREVIEW in a deployed environment.


🛠️ Full setup (Supabase)

  1. Create a project at supabase.com — the free tier is plenty.

  2. Run the migrations. Paste the files in supabase/migrations/ into the Supabase SQL editor in order, or use the CLI (supabase db push):

    Migration What it does
    0001_init.sql Tables, enums, indexes, RLS policies, new-user trigger.
    0002_storage.sql avatars, designs (public) and tattoos (private) buckets + policies.
    0003_hardening.sql Security hardening (pins search_path, locks down the bootstrap function).
    0004_inkdex.sql The inkdex_entries table + the private inkdex bucket, all owner-scoped.
    0005_security_hardening.sql Owner-only profile/storage policies and atomic generation quota reservation.
    0006_inkdex_explicit_sighting.sql Explicit photo-and-story guard for Inkdex inserts.
    0007_artist_shop_directory.sql Public-read, service-role-write directory tables and copyright-safe style pieces.
    0008_appointment_requests.sql Owner-scoped appointment requests and lifecycle indexes.
  3. Fill in .env.local:

    NEXT_PUBLIC_SUPABASE_URL=https://<ref>.supabase.co
    NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon / publishable key>
    SUPABASE_SERVICE_ROLE_KEY=<service role key>   # server-only; needed for seeding
  4. Harden Storage after applying the migrations. This makes private media buckets private, applies the allowed image MIME types/size limit, and checks their owner-scoped policies:

    npm run harden:storage
  5. Seed the gallery (curated designs, made by the built-in engine — no image API needed):

    npm run seed
  6. Optionally seed the small, attributable directory starter catalog. It contains no scraped portfolio images:

    npm run seed:directory
  7. Restart npm run dev, create an account, complete onboarding, and you're in.

OAuth (Google / GitHub) buttons appear automatically once Supabase is configured. Enable the providers in Supabase → Authentication → Providers and add <site>/auth/callback as a redirect URL.


🤖 AI image generation

The generator is provider-agnostic. With AI_PROVIDER unset (or local), Inkai resolves a default chain automatically and tries each in order until one works — so generation never hard-fails:

  1. Gemini ("Nano Banana", gemini-2.5-flash-image) — used if a GEMINI_API_KEY is set. Best quality. (Note: Gemini image generation now requires a billing-enabled Google Cloud project.)
  2. Pollinationskeyless, zero setup. This is what produces real raster tattoo art out of the box, before you touch any config.
  3. Local — a built-in procedural vector "flash" engine. The unconditional last resort, so a result always comes back.

The UI shows which provider actually produced each result. To use a specific paid provider instead, set AI_PROVIDER=openai (OPENAI_API_KEY) or AI_PROVIDER=replicate (REPLICATE_API_TOKEN).

The Canvas can optionally ask Gemini for one fixed body-zone suggestion from a photo. It uses the same GEMINI_API_KEY, defaults to GEMINI_VISION_MODEL=gemini-2.5-flash, and always requires the member to confirm or correct the suggested placement before saving.

Cost & abuse control: each user is capped at DAILY_GENERATION_LIMIT (default 30) real generations per day, rapid-fire requests are throttled, and identical recent requests are served from cache instead of paying for a new call. Remote images are re-hosted in Supabase Storage so links never expire.


☁️ Deploy to Vercel

  1. Push to GitHub and import the repo at vercel.com/new — Next.js is auto-detected, no build config needed.

  2. Add environment variables (Project → Settings → Environment Variables):

    Variable Value
    NEXT_PUBLIC_SUPABASE_URL https://<ref>.supabase.co
    NEXT_PUBLIC_SUPABASE_ANON_KEY your anon / publishable key
    SUPABASE_SERVICE_ROLE_KEY service-role key (server-only; for seeding)
    GEMINI_API_KEY (optional) for best-quality generation
    GOOGLE_MAPS_API_KEY (optional, server-only) Geocoding + Places API (New) nearby search
    RESEND_API_KEY (optional, server-only) appointment request email hand-off
    RESEND_FROM_EMAIL (optional) verified Resend sender, e.g. Inkai <bookings@example.com>
    NEXT_PUBLIC_SITE_URL canonical production URL for auth redirects

    Generation works with no key, so you can ship first and add a key later.

    The directory still works without Maps; it returns only curated records. Appointment requests still save without Resend and show the target's direct booking contact instead. Never set INKAI_PREVIEW=1 in Vercel or any other deployed environment.

  3. Deploy once, then set NEXT_PUBLIC_SITE_URL to your production URL and redeploy — this is what auth redirects point at.

  4. Point Supabase auth at production: in Authentication → URL Configuration, add https://<your-domain>/auth/callback as a redirect URL.

  5. Run migrations 0001 through 0008 in order, then run npm run harden:storage, npm run seed, and optionally npm run seed:directory. The migrations are idempotent; keep the service role key server-only and never add it to a browser variable.


📁 Project structure

src/
  app/
    (marketing)/   # public landing
    (auth)/        # login / signup
    (app)/         # authenticated shell: dashboard, gallery, generate,
                   # collections, my-ink, canvas, directory, appointments,
                   # inkdex, settings
    onboarding/    # style-profile quiz
  components/      # ui/ primitives, features/, app shell, marketing…
  lib/
    ai/            # provider chain (gemini, pollinations, openai, replicate)
                   # + procedural SVG engine fallback
    supabase/      # browser / server / admin clients + typed schema
    constants.ts   # the tattoo taxonomy (styles, placements, motifs…)
  server/
    queries/       # read-side data access (server-only)
    actions/       # 'use server' mutations
  proxy.ts         # Next 16 middleware (session refresh + route protection)
supabase/migrations/   # SQL schema, RLS, storage

Scripts

Script Purpose
npm run dev Development server (Turbopack).
npm run build Production build.
npm run seed Seed curated public designs (needs SUPABASE_SERVICE_ROLE_KEY).
npm run seed:directory Seed the attributable Artist & Shop Directory starter catalog.
npm run harden:storage Apply private-bucket configuration and storage policy checks.
npm run typecheck tsc --noEmit.
npm run lint ESLint.

Built with care. Ink that feels like you.

About

AI-powered tattoo inspiration app — personalised style profiles, real AI-generated designs, collections, and the Inkdex (a collectible catalog of tattoos spotted in the wild). Next.js 16 + Supabase.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages