first commit

This commit is contained in:
Carlos
2026-02-21 10:31:46 +01:00
parent 17ca405add
commit c863a943ed
146 changed files with 24563 additions and 90 deletions

201
CONTEXT.md Normal file
View File

@@ -0,0 +1,201 @@
# 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`)
```typescript
// 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`)
```typescript
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