Skip to content

Latest commit

 

History

History
112 lines (78 loc) · 5.71 KB

File metadata and controls

112 lines (78 loc) · 5.71 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

# Development
npm run dev          # Start Next.js dev server

# Build & Production
npm run build        # Build for production
npm run start        # Start production server

# Code Quality
npm run lint         # Run ESLint

# Type checking (no tests exist)
npx tsc --noEmit

# Docker
npm run docker:compose:up    # Start with Docker Compose (detached)
npm run docker:compose:down  # Stop Docker Compose
npm run docker:compose:logs  # Tail Docker Compose logs
npm run docker:build         # Build Docker image
npm run docker:run           # Run Docker container on :3000

Architecture

Retouchly is a Next.js 16 App Router SaaS for AI-powered image editing. Everything is in a single Next.js monorepo — no separate backend service.

Key directories

  • src/app/ — Pages and server actions (App Router)
    • (tools)/ — Route group for AI tools: background-remover, colorization, face-restoration, image-generation, image-overlay, text-to-speech
    • actions/ — Server Actions for all AI calls and Supabase writes
    • api/replicate-status/[id]/ — Polling endpoint for async Replicate predictions
    • auth/ — Auth callback and email verification routes
    • dashboard/, history/, explore/, pricing/, profile/[id]/, settings/ — User-facing pages
  • src/components/ — React components grouped by feature; ui/ contains shadcn/ui primitives
  • src/store/useGeneratedStore.ts — Single Zustand store for all AI tool state
  • src/lib/ — Supabase client, OpenAI client, and client-side helpers for community/follow mutations
  • src/hooks/ — React hooks (useNotifications.ts for Supabase Realtime)
  • src/types/ — Shared TypeScript types (e.g. notifications.ts)

Data flow for AI tools

Every AI tool follows the same two-function pattern per action file:

  1. create*Prediction(input) — Server Action that calls replicate.predictions.create(...) and returns { predictionId, success, error }. Replicate client must be instantiated with useFileOutput: false.
  2. *WithAI(input) — Legacy blocking version using replicate.run(...) kept for reference but not used by the store.

In the Zustand store (useGeneratedStore.ts):

  • Components call store methods (never Server Actions directly)
  • Store calls create*Prediction() → gets predictionId → calls get().startPolling(predictionId, onComplete, onError)
  • startPolling polls /api/replicate-status/[id] every 1500ms; clears any prior timer before starting a new one (the timer is closure-scoped inside create() to avoid cross-tool cancellation)
  • For tool functions returning Promise<string | null>, the polling callbacks are wrapped in new Promise<string | null>

Credits system

Each AI operation deducts 2 credits via deductCredit(userId) in src/app/actions/credits-actions.ts. This calls the deduct_credits Supabase RPC (defined as SECURITY DEFINER to bypass RLS). Pro plan users get Infinity credits. Always call deductCredit before starting a prediction and bail out with toast.error if credit.ok is false.

Community / social layer

The explore/ page has a deliberate client/server split:

  • Client helpers (src/lib/community-client.ts, src/lib/follow-client.ts) — run in the browser against Supabase RLS; used for like/follow mutations
  • Server Actions (src/app/actions/community-actions.ts, follow-actions.ts) — used for reads needing server-side filtering or aggregation

Like counts are maintained by the increment_like_count Supabase RPC; fallback to manual fetch+update if the RPC is absent.

Notification system

DB triggers (trg_notify_follow, trg_notify_like, trg_notify_comment) write to the notifications table. The useNotifications(userId) hook in src/hooks/useNotifications.ts subscribes to Supabase Realtime (postgres_changes INSERT on notifications filtered by recipient_id) and re-fetches on each event. NotificationBell in the Navbar consumes this hook.

Onboarding tour

OnboardingTour (injected in src/app/layout.tsx) uses localStorage key retouchly_show_onboarding as its trigger. Set this key to "true" (e.g. after signup in SignUpDialog) to show the tour on next render. Target elements are identified by data-tour attributes on Navbar elements: ai-tools, explore, credits, referral. The Settings page has a "Restart Tour" button that sets the key and reloads.

Supabase client

There is a single anon-key client at src/lib/supabase.ts used everywhere — both in Server Actions and client components. There is no separate service-role client. Operations that require elevated access use SECURITY DEFINER RPCs.

Styling

Tailwind CSS v4 with CSS variables in oklch color format. Dark mode via .dark class. shadcn/ui uses the new-york variant. Path alias @/* maps to src/*.

External integrations

Service Purpose Notes
Supabase Auth, DB, Storage, Realtime Single anon client; elevated ops via SECURITY DEFINER RPCs
Replicate All image/voice AI models Always useFileOutput: false; always use predictions.create() not run() for new features
OpenAI GPT-4 AI assistant suggestions src/lib/openai.ts
iyzipay Payments (Turkish provider) Listed as serverExternalPackages in next.config.ts

Environment variables

Copy .env.example to .env.local:

OPENAI_API_KEY=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
REPLICATE_API_TOKEN=
IYZICO_API_KEY=
IYZICO_API_SECRET=
IYZICO_BASE_URL=

No tests

There is no test infrastructure in this project. Use npx tsc --noEmit to catch type errors.