managarten/apps-archived/maerchenzauber/CLAUDE.md
Till-JS 61d181fbc2 chore: archive inactive projects to apps-archived/
Move inactive projects out of active workspace:
- bauntown (community website)
- maerchenzauber (AI story generation)
- memoro (voice memo app)
- news (news aggregation)
- nutriphi (nutrition tracking)
- reader (reading app)
- uload (URL shortener)
- wisekeep (AI wisdom extraction)

Update CLAUDE.md documentation:
- Add presi to active projects
- Document archived projects section
- Update workspace configuration

Archived apps can be re-activated by moving back to apps/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 07:03:59 +01:00

17 KiB

CLAUDE.md

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

Project Overview

Storyteller is a magical children's story generation app with:

  • AI-powered story creation with consistent characters
  • Custom character generation from descriptions or photos
  • Multi-page illustrated stories
  • Credit-based system via Mana Core
  • Support for multiple languages (German/English)

Prerequisites

  • Node.js 18+ and pnpm 9+
  • Install pnpm globally: npm install -g pnpm
  • Supabase account and project
  • Mana Core account for authentication
  • AI API keys (Azure OpenAI, Gemini, Replicate)

Quick Start

Local Development Setup (Mobile + Local Backend)

Step 1: Configure mobile to use local backend

# Edit apps/mobile/.env and set:
echo "EXPO_PUBLIC_STORYTELLER_BACKEND_URL=http://localhost:3002" > apps/mobile/.env
echo "EXPO_ROUTER_APP_ROOT=app" >> apps/mobile/.env

# For physical devices, use your computer's IP instead:
# EXPO_PUBLIC_STORYTELLER_BACKEND_URL=http://192.168.1.X:3002

Step 2: Start backend and mobile

# Terminal 1 - Backend
cd apps/backend && npm run dev
# Backend runs on http://localhost:3002
# Wait for "Nest application successfully started" message

# Terminal 2 - Mobile
cd apps/mobile && npm run dev
# Press 'i' for iOS, 'a' for Android, 'w' for web

Step 3: Verify connection

# In Terminal 1 (backend), you should see incoming requests from mobile app
# Check health endpoint:
curl http://localhost:3002/health | jq

Alternative: Mobile with Production Backend

# Edit apps/mobile/.env to use production:
echo "EXPO_PUBLIC_STORYTELLER_BACKEND_URL=https://storyteller-backend-111768794939.europe-west3.run.app" > apps/mobile/.env
echo "EXPO_ROUTER_APP_ROOT=app" >> apps/mobile/.env

# Restart Expo with cache clear (important!)
cd apps/mobile && npx expo start -c

Quick Backend Switch Reference

Setup apps/mobile/.env Value Terminal Setup
Local Backend http://localhost:3002 Run both backend + mobile
Physical Device http://YOUR_IP:3002 Run both backend + mobile
Production Backend https://storyteller-backend-111768794939.europe-west3.run.app Run mobile only

Remember: Always restart Expo with -c flag after changing .env

Other Development Commands

Install dependencies (first time):

pnpm install

Start everything via Turborepo:

pnpm run dev  # From root - starts all services

Backend only:

cd apps/backend && npm run dev
# Test: curl http://localhost:3002/health | jq

Mobile only:

cd apps/mobile && npm run dev

Landing page:

cd apps/landing && npm run dev

Web app:

cd apps/web && npm run dev

Project Structure

apps/maerchenzauber/
├── apps/
│   ├── backend/             # NestJS backend
│   │   ├── src/
│   │   │   ├── story/       # Story generation & management
│   │   │   │   ├── services/story-creation.service.ts  # Main story generation logic
│   │   │   │   └── illustration.service.ts             # Image generation
│   │   │   ├── character/   # Character creation & management
│   │   │   │   └── character.service.ts
│   │   │   ├── core/
│   │   │   │   ├── services/
│   │   │   │   │   ├── prompting.service.ts           # AI prompt management
│   │   │   │   │   ├── image-supabase.service.ts      # Image storage
│   │   │   │   │   └── supabase-data.service.ts       # Database operations
│   │   │   │   └── consts/ # System prompts & constants
│   │   │   ├── settings/   # User settings
│   │   │   ├── credits/    # Credit management
│   │   │   ├── auth/       # Authentication proxy
│   │   │   └── health/     # Health checks
│   │   └── .env            # Backend configuration
│   ├── mobile/             # React Native Expo app
│   │   ├── app/            # expo-router routes
│   │   │   └── (tabs)/
│   │   │       ├── (story)/  # Story screens
│   │   │       └── (character)/ # Character screens
│   │   ├── src/
│   │   │   ├── utils/api.ts # API client
│   │   │   ├── services/
│   │   │   │   ├── authService.ts
│   │   │   │   └── tokenManager.ts
│   │   │   ├── components/ # Reusable components
│   │   │   └── hooks/      # Custom React hooks
│   │   └── .env            # Mobile configuration
│   ├── landing/            # Astro landing page
│   └── web/                # SvelteKit web app (NEW)
├── docs/                   # Documentation
│   └── SYSTEM_CHARACTERS.md # System characters guide
├── turbo.json              # Turborepo configuration
└── package.json            # Root package with workspace scripts

Key Implementation Details

Story Generation Flow

  1. Character Selection: User selects existing character or creates new one
  2. Story Request: User provides story description, system selects author/illustrator personas
  3. Generation Pipeline:
    • Generate story text (10 pages) via story-creation.service.ts
    • Extract character descriptions
    • Generate illustration prompts
    • Create images via Replicate
    • Translate to German
    • Save to database
  4. Credit Deduction: 10 credits per story/character via Mana Core

System Characters

System characters are special read-only characters visible to all users:

  • Identified by system user ID: 00000000-0000-0000-0000-000000000000
  • Accessible to all authenticated users via RLS policies
  • Cannot be edited or deleted by regular users
  • UI hides edit/share buttons for system characters
  • Currently includes "Finia" the wise fox as the default character

See detailed documentation: docs/SYSTEM_CHARACTERS.md

Key implementation points:

  • Database: Characters table with system user_id
  • Backend: Validation allows both owned and system characters
  • Frontend: Conditional rendering based on user_id check
  • Storage: Images stored in system user folder in Supabase Storage

AI Services Architecture

  • Prompting: backend/src/core/services/prompting.service.ts handles all AI interactions
  • System Prompts: Stored in backend/src/core/consts/
  • Image Generation: backend/src/core/services/image-supabase.service.ts
  • Storage: Supabase Storage bucket maerchenzauber
  • Models: Azure OpenAI (GPT-4), Google Gemini, Replicate (Flux for images)

Database (Supabase)

  • Client: backend/src/supabase/supabase.provider.ts
  • Data Service: backend/src/core/services/supabase-data.service.ts
  • Tables: characters, stories, story_collections, user_settings

Authentication

  • Uses Mana Core for authentication
  • Backend acts as proxy: backend/src/auth/
  • Mobile token management: mobile/src/services/tokenManager.ts
  • Secure storage: expo-secure-store for tokens

Package Manager

This project uses PNPM as the package manager for better performance and disk space efficiency.

Key PNPM commands:

pnpm install              # Install all dependencies
pnpm add <package>        # Add a dependency to workspace root
pnpm add <package> -w     # Add to workspace root (explicit)
pnpm add <package> --filter @storyteller/backend  # Add to specific app

Available Scripts

Root Level

  • pnpm run dev - Start all services (Turborepo)
  • pnpm run build - Build all applications
  • pnpm run lint - Lint all packages
  • pnpm run type-check - TypeScript checks
  • pnpm run format - Format with Prettier
  • pnpm run clean - Clean build artifacts

Backend (cd apps/backend)

  • pnpm run dev - Start with hot reload (port 3002)
  • pnpm run build - Build production bundle
  • pnpm run start:prod - Run production build
  • pnpm run test - Run Jest tests
  • pnpm run test:watch - Tests in watch mode
  • pnpm run test:cov - Tests with coverage
  • pnpm run type-check - TypeScript validation

Mobile (cd apps/mobile)

  • pnpm run dev - Start Expo dev server
  • pnpm run ios - Run on iOS simulator
  • pnpm run android - Run on Android emulator
  • pnpm run web - Run in browser
  • pnpm run build - Export production build
  • pnpm run test - Run tests
  • pnpm run type-check - TypeScript validation

Landing Page (cd apps/landing)

  • pnpm run dev - Start Astro dev server
  • pnpm run build - Build static site
  • pnpm run preview - Preview production build

Web App (cd apps/web)

  • pnpm run dev - Start SvelteKit dev server
  • pnpm run build - Build production bundle
  • pnpm run preview - Preview production build

Troubleshooting

Backend Won't Start

# Port 3002 already in use
lsof -i :3002
kill -9 $(lsof -t -i:3002)

# Check environment variables
cat backend/.env | grep -v "^#" | grep -v "^$"

# Database connection issues - verify:
# - MAERCHENZAUBER_SUPABASE_URL
# - MAERCHENZAUBER_SUPABASE_ANON_KEY
# - MAERCHENZAUBER_SUPABASE_SERVICE_ROLE_KEY

Mobile Can't Connect to Backend

# For physical devices, use computer IP instead of localhost
ifconfig | grep "inet " | grep -v 127.0.0.1

# Update apps/mobile/.env
EXPO_PUBLIC_STORYTELLER_BACKEND_URL=http://YOUR_IP:3002

# IMPORTANT: After changing .env, restart Expo dev server
cd apps/mobile && npx expo start -c  # -c flag clears cache

Environment Variables Not Loading

# Mobile app: Environment changes require restart
# 1. Stop Expo dev server (Ctrl+C)
# 2. Clear cache and restart:
cd apps/mobile && npx expo start -c

# Backend: Changes require restart
# 1. Stop backend (Ctrl+C)
# 2. Restart:
cd apps/backend && npm run dev

# Verify environment is loaded (backend):
# Check logs on startup - you should see configuration being loaded

AI Service Errors

Check these environment variables in apps/backend/.env:

  • MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT and _KEY
  • MAERCHENZAUBER_GOOGLE_GENAI_API_KEY
  • MAERCHENZAUBER_REPLICATE_API_KEY

If the app crashes when opening deeplinks on iOS (especially character sharing links):

See complete debugging guide for detailed solutions.

Quick fixes:

  1. Check expo-linear-gradient version is 15.0.7+
  2. Ensure all LinearGradient styles use alignSelf: 'stretch' instead of width: '100%'
  3. Verify MagicalLoadingScreen has context prop
  4. Check expo-image has transition={0} on navigation screens
  5. Ensure each route directory has proper _layout.tsx with freezeOnBlur: false

Common symptoms:

  • Crash during navigation after deeplink opens
  • CoreGraphics CGContextDrawLinearGradient errors
  • RNSScreen setViewToSnapshot crashes
  • Image deallocation errors

API Testing

Health Checks

# Full health check
curl http://localhost:3002/health | jq

# Readiness check (for deployments)
curl http://localhost:3002/health/ready | jq

# Liveness check
curl http://localhost:3002/health/live | jq

Main Endpoints

# Authentication
POST /auth/signin           # Sign in with email/password
POST /auth/signup           # Create account
POST /auth/google-signin    # Google OAuth
POST /auth/apple-signin     # Apple Sign In
POST /auth/refresh          # Refresh token
POST /auth/logout           # Log out

# Characters
GET  /character             # List user's characters
POST /character/generate-images  # Generate character
GET  /character/:id         # Get specific character
PUT  /character/:id         # Update character
DELETE /character/:id       # Delete character

# Stories
GET  /story                 # List user's stories
POST /story                 # Create new story
GET  /story/:id             # Get specific story
PUT  /story/:id             # Update story
DELETE /story/:id           # Delete story

# Settings
GET  /settings/user         # User settings
PUT  /settings/user         # Update settings
GET  /settings/creators     # Available creators
GET  /settings/languages    # Supported languages

# Credits
GET  /credits/balance       # Get balance
GET  /credits/history       # Transaction history
POST /credits/check         # Check availability
POST /credits/consume       # Consume credits

Environment Variables

Backend (backend/.env)

Required:

  • MANA_SERVICE_URL - Mana Core auth service URL
  • APP_ID - Mana Core application ID
  • SERVICE_KEY - Mana Core service key
  • MAERCHENZAUBER_SUPABASE_URL - Supabase project URL
  • MAERCHENZAUBER_SUPABASE_ANON_KEY - Supabase anonymous key
  • MAERCHENZAUBER_SUPABASE_SERVICE_ROLE_KEY - Supabase service role key
  • MAERCHENZAUBER_AZURE_OPENAI_ENDPOINT - Azure OpenAI endpoint
  • MAERCHENZAUBER_AZURE_OPENAI_KEY - Azure OpenAI key
  • MAERCHENZAUBER_GOOGLE_GENAI_API_KEY - Google Gemini API key
  • MAERCHENZAUBER_REPLICATE_API_KEY - Replicate API token
  • MAERCHENZAUBER_STORAGE_BUCKET - Supabase storage bucket name (default: maerchenzauber)

Optional:

  • PORT - Server port (default: 3002)
  • NODE_ENV - Environment (development/production)

Mobile (apps/mobile/.env)

Required:

  • EXPO_PUBLIC_STORYTELLER_BACKEND_URL - Backend API URL
    • Local: http://localhost:3002
    • Production: https://storyteller-backend-111768794939.europe-west3.run.app
    • Physical devices: http://YOUR_COMPUTER_IP:3002
  • EXPO_ROUTER_APP_ROOT - App root (always app)

Code Patterns & Standards

Backend (NestJS/TypeScript)

  • Dependency Injection: Use NestJS DI with @Injectable() decorators
  • Error Handling: Return Result<T> type: { data: T | null, error: Error | null }
  • DTOs: Use class-validator decorators for validation
  • Logging: Use console.log with service prefix: console.log('[ServiceName]', 'message', data)
  • Database: All queries through supabase-data.service.ts

Mobile (React Native/Expo)

  • Components: Functional components with hooks only
  • Navigation: Use expo-router file-based routing
  • API Calls: Through src/utils/api.ts with automatic token refresh
  • Storage: Use expo-secure-store for sensitive data, @react-native-async-storage/async-storage for non-sensitive
  • State: Local state with useState, no global state library currently

Error Handling Pattern

// Backend
async function operation(): Promise<Result<Data>> {
	try {
		const result = await doSomething();
		return { data: result, error: null };
	} catch (error) {
		console.error('[ServiceName] Operation failed:', error);
		return { data: null, error };
	}
}

// Mobile
const response = await fetchWithAuth('/endpoint', {
	method: 'POST',
	body: JSON.stringify(data),
});
if (!response.ok) {
	throw new Error(`API error: ${response.status}`);
}

Deployment

Backend (Google Cloud Run)

Current production: https://storyteller-backend-111768794939.europe-west3.run.app

cd apps/backend

# Build and test locally
docker build -t storyteller-backend .
docker run -p 3002:3002 --env-file .env storyteller-backend

# Deploy to Cloud Run
gcloud builds submit --tag gcr.io/PROJECT_ID/storyteller-backend
gcloud run deploy storyteller-backend \
  --image gcr.io/PROJECT_ID/storyteller-backend \
  --region europe-west3 \
  --platform managed \
  --allow-unauthenticated

Mobile (EAS Build)

EAS configuration in eas.json at project root.

cd apps/mobile

# Configure EAS (first time)
eas build:configure

# Build for iOS
eas build --platform ios --profile production

# Build for Android
eas build --platform android --profile production

# Submit to stores
eas submit --platform ios
eas submit --platform android

Landing Page (Netlify/Vercel)

cd apps/landing

# Build locally
npm run build

# Deploy (automatically via git push to main)

Web App (SvelteKit)

cd apps/web

# Build locally
npm run build

# Preview production build
npm run preview

Debugging

Backend Debugging

# Check logs in production
gcloud run services logs read storyteller-backend --limit=100

# Monitor specific service
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=storyteller-backend" --limit=50

Mobile Debugging

  • Network: Check API calls in apps/mobile/src/utils/api.ts
  • Storage: Use React Native Debugger to inspect AsyncStorage/SecureStore
  • Logs: Use console.log('[ComponentName]', message) format
  • Expo: Use npx expo start --dev-client for more debugging tools

Database Debugging

  • Use Supabase Dashboard for direct queries
  • Add .select('*') to see all returned fields
  • Check RLS policies if queries return empty results