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:
- Idea Motriz — Concepto, estructura narrativa, valores, protagonistas
- Personajes — Fichas completas (especie, apariencia física, personalidad, utilería visual…)
- 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.localDEBE estar en UTF-8 sin BOM. Si se guarda como UTF-16 LE (bytes iniciales255,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)
- Generar prompts → OpenAI GPT-4o genera 9 prompts (3 T-poses + 6 emociones) basándose en la ficha del personaje
- Generar imagen base → Gemini text-to-image genera la imagen canónica (T-pose frontal). El prompt es editable antes de generar.
- Fijar como referencia → El usuario revisa y "bloquea" la imagen base como referencia canónica
- 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 conawait params - API Routes:
NextRequest/NextResponse, patrón RESTful con acciones viaactionen body POST - Archivos de datos: Markdown con frontmatter YAML (via
gray-matter) + JSON auxiliar - Estilo UI: TailwindCSS 4, diseño minimalista con paleta
stone, acentosindigoyemerald - Componentes UI:
Button(variantes: primary/secondary/outline/ghost/danger),Card,Input/Textarea,Select,Toast - Sin base de datos: Todo se lee/escribe en
projects/confs/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_KEYconfigurada) - ⏳ 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
- 0 prompts generados: Los
planosno se guardaban como JSON separado → ahora se guardan enescaleta-planos.json - OPENAI_API_KEY no leída: El
.env.localestaba en UTF-16 LE con BOM → convertido a UTF-8 sin BOM - Import dinámico rompía env vars con Turbopack: Cambiado a import estático en API routes