Files
story-studio/CONTEXT.md
2026-02-21 10:31:46 +01:00

8.8 KiB

Story Studio — Contexto para Claude Code

Qué es este proyecto

Story Studio es una aplicación Next.js (v16, App Router, Turbopack) para crear series animadas infantiles de forma asistida por IA. El flujo de trabajo es un wizard de 3 pasos:

  1. Idea Motriz — Concepto, estructura narrativa, valores, protagonistas
  2. Personajes — Fichas completas (especie, apariencia física, personalidad, utilería visual…)
  3. Capítulos — Guión conductor → Escaleta (desglose de planos) → Prompts de imagen → Assets de personaje

Los datos se persisten como archivos markdown + JSON en projects/{slug}/.


Stack técnico

Capa Tecnología
Framework Next.js 16.1.6 (App Router, use() para params)
React 19.2.3
Estilos TailwindCSS 4 + PostCSS
Iconos Lucide React
LLM (texto) OpenAI GPT-4o (openai SDK)
LLM (imágenes) Google Gemini / Nano Banana Pro (@google/genai SDK)
Datos Archivos en disco (projects/ dir), sin base de datos

Variables de entorno (.env.local)

OPENAI_API_KEY=sk-...
GEMINI_API_KEY=AI...

IMPORTANTE: El .env.local DEBE estar en UTF-8 sin BOM. Si se guarda como UTF-16 LE (bytes iniciales 255,254), Next.js no puede parsear las variables.


Estructura de archivos

story-studio/
├── src/
│   ├── app/
│   │   ├── page.tsx                          # Home — lista de proyectos
│   │   ├── project/[slug]/
│   │   │   ├── page.tsx                      # Wizard principal (3 pasos)
│   │   │   └── capitulo/[numero]/page.tsx    # Editor de capítulo
│   │   └── api/projects/[slug]/
│   │       ├── route.ts                      # CRUD proyecto
│   │       ├── idea-motriz/route.ts          # Generar/refinar idea
│   │       ├── personajes/
│   │       │   ├── route.ts                  # CRUD personajes
│   │       │   └── [id]/
│   │       │       ├── assets/route.ts       # ★ Character assets API
│   │       │       └── images/[assetId]/route.ts  # Servir imágenes desde disco
│   │       └── capitulos/
│   │           ├── route.ts
│   │           └── [numero]/
│   │               ├── guion/route.ts
│   │               ├── escaleta/route.ts
│   │               └── prompts/route.ts
│   ├── components/
│   │   ├── project/
│   │   │   ├── CharacterAssetsPanel.tsx      # ★ Panel de assets con workflow
│   │   │   ├── PersonajesView.tsx
│   │   │   ├── PersonajeEditor.tsx
│   │   │   ├── IdeaMotrizView.tsx
│   │   │   └── IdeaMotrizEditor.tsx
│   │   ├── ui/   (Button, Card, Input, Select, Toast)
│   │   └── wizard/ (WizardStepper, GenerationPanel)
│   ├── hooks/
│   │   └── useProjectData.ts                 # Hook principal del wizard
│   ├── lib/
│   │   ├── file-service.ts                   # Lectura/escritura de archivos de proyecto
│   │   ├── llm-service.ts                    # OpenAI: generación de texto (ideas, personajes, guiones, prompts)
│   │   ├── image-service.ts                  # ★ Gemini: generación de imágenes (text2img + img2img)
│   │   └── prompts.ts                        # Todos los system prompts para el LLM
│   └── types/
│       └── project.ts                        # Todos los tipos TypeScript
├── projects/                                 # Datos persistidos en disco
│   └── {slug}/
│       ├── project.md
│       ├── idea-motriz.md
│       ├── personajes.md
│       ├── personajes/{id}/
│       │   ├── assets.json                   # CharacterAssetsData
│       │   └── images/{assetId}.png          # Imágenes generadas
│       └── capitulos/{numero}/
│           ├── capitulo.md
│           ├── guion-conductor.md
│           ├── escaleta.md
│           ├── escaleta-planos.json          # Planos estructurados
│           └── prompts.md
└── package.json

Sistema de Character Assets (última feature implementada)

Flujo de trabajo (4 pasos en UI)

  1. Generar prompts → OpenAI GPT-4o genera 9 prompts (3 T-poses + 6 emociones) basándose en la ficha del personaje
  2. Generar imagen base → Gemini text-to-image genera la imagen canónica (T-pose frontal). El prompt es editable antes de generar.
  3. Fijar como referencia → El usuario revisa y "bloquea" la imagen base como referencia canónica
  4. Generar variaciones → Gemini image-to-image genera el resto de poses/emociones usando la imagen base como referencia

API: /api/projects/[slug]/personajes/[id]/assets

GET → Devuelve CharacterAssetsData { assets: CharacterAsset[], baseLocked: boolean }

POST con action:

Acción Descripción
generate-prompts LLM genera prompts de texto para 9 assets
generate-base-image Text-to-image con Gemini (acepta prompt override)
lock-base Fija la imagen base como referencia
generate-variation Img-to-img de un asset individual (requiere assetId)
generate-all-variations Genera todas las variaciones pendientes secuencialmente

Servicio de imágenes (image-service.ts)

// Text-to-image (para imagen base)
generateImage(prompt: string, options?: { aspectRatio?: string }): Promise<Buffer>

// Image-to-image (para variaciones desde referencia)
generateVariation(referenceImageBuffer: Buffer, prompt: string, options?): Promise<Buffer>

// Storage
saveImage(slug, personajeId, assetId, buffer): Promise<string>  // returns URL
loadImage(slug, personajeId, assetId): Promise<Buffer | null>

Usa modelo gemini-2.5-flash-preview-image-generation con responseModalities: ['TEXT', 'IMAGE'].

Tipos clave (types/project.ts)

interface CharacterAsset {
  id: string;               // "{personajeId}-{type}-{variant}"
  type: 'tpose' | 'emotion' | 'pose';
  variant: string;          // 'front'|'side'|'back' o 'alegria'|'tristeza'|etc.
  label: string;
  prompt: string;
  imageUrl?: string;        // "/api/projects/{slug}/personajes/{id}/images/{assetId}"
  isBase?: boolean;         // true for canonical reference
  status: 'pending' | 'prompt_ready' | 'generating' | 'generated';
}

interface CharacterAssetsData {
  assets: CharacterAsset[];
  baseLocked: boolean;
}

UI (CharacterAssetsPanel.tsx)

  • Modal a pantalla completa con stepper visual (4 pasos)
  • Sección de imagen base con preview, editor de prompt, botones generar/regenerar/fijar
  • Grid de variaciones (T-poses y emociones) con generación individual o masiva
  • Se abre desde PersonajesView.tsx → botón "Assets" por personaje

Convenciones del proyecto

  • Next.js App Router: Todos los params son Promise<> y se resuelven con await params
  • API Routes: NextRequest / NextResponse, patrón RESTful con acciones via action en body POST
  • Archivos de datos: Markdown con frontmatter YAML (via gray-matter) + JSON auxiliar
  • Estilo UI: TailwindCSS 4, diseño minimalista con paleta stone, acentos indigo y emerald
  • Componentes UI: Button (variantes: primary/secondary/outline/ghost/danger), Card, Input/Textarea, Select, Toast
  • Sin base de datos: Todo se lee/escribe en projects/ con fs/promises

Estado actual

  • Wizard completo: idea motriz → personajes → capítulos
  • Generación de guión conductor y escaleta
  • Prompts de imagen por plano (lee planos desde JSON server-side)
  • Character assets: prompts generados por LLM
  • Character assets: generación de imagen base con Gemini (text-to-image)
  • Character assets: flujo lock-base + variaciones con referencia (img-to-image)
  • Pendiente: Probar flujo completo de generación de imágenes (requiere GEMINI_API_KEY configurada)
  • Pendiente: Las imágenes de los prompts de planos (escaleta) aún no se generan con Gemini
  • Pendiente: Integrar assets de personaje con la composición final en Remotion

Proyecto hermano: Remotion (../vibecoding/)

Existe un proyecto Remotion en ../vibecoding/ que es donde se componen los videos finales. Usa las imágenes generadas aquí. Su package.json incluye remotion 4.0.409, openai, dotenv, y tailwindcss 4.0.0.


Bugs resueltos recientemente

  1. 0 prompts generados: Los planos no se guardaban como JSON separado → ahora se guardan en escaleta-planos.json
  2. OPENAI_API_KEY no leída: El .env.local estaba en UTF-16 LE con BOM → convertido a UTF-8 sin BOM
  3. Import dinámico rompía env vars con Turbopack: Cambiado a import estático en API routes