import React, { useState } from "react";
// Componente principal de la aplicación
export default function App() {
const [imageFile, setImageFile] = useState(null);
const [base64ImageData, setBase64ImageData] = useState(null);
const [prompt, setPrompt] = useState("Hacer que el hombre de la imagen se ponga de pie");
const [generatedImage, setGeneratedImage] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// "edit" = editar imagen subida, "generate" = crear desde cero
const [mode, setMode] = useState("edit");
/**
* Maneja el cambio en el input de archivo.
* Convierte la imagen seleccionada a base64.
*/
const handleFileChange = (event) => {
const file = event.target.files[0];
if (file) {
setImageFile(file);
setError(null);
setGeneratedImage(null); // Limpiar imagen generada anteriormente
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result
.replace("data:", "")
.replace(/^.+,/, "");
setBase64ImageData(base64String);
};
reader.onerror = () => {
setError("Error al leer el archivo de imagen.");
};
reader.readAsDataURL(file);
}
};
/**
* Llama a la API de Gemini para generar una imagen.
* Ahora soporta:
* - Modo "edit": prompt + imagen subida
* - Modo "generate": solo prompt (imagen desde cero)
* En ambos casos: estilo Pixar 3D, 4K, formato vertical 9:16
*/
const handleGenerateImage = async () => {
// Validaciones según el modo
if (!prompt) {
setError("Por favor, escribe una instrucción.");
return;
}
if (mode === "edit" && !base64ImageData) {
setError("En modo EDICIÓN debes subir una imagen.");
return;
}
setLoading(true);
setError(null);
setGeneratedImage(null);
// ⚠️ Pon aquà tu API key (idealmente vÃa variable de entorno)
const apiKey = "";
if (!apiKey) {
setError("Falta la API Key de Gemini. Configúrala en el código o en variables de entorno.");
setLoading(false);
return;
}
// Se recomienda usar el modelo estable gemini-2.5-flash-image
const modelId = "gemini-2.5-flash-image";
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${modelId}:generateContent?key=${apiKey}`;
// Añadimos siempre el estilo Pixar 3D, 4K, vertical
const styleSuffix =
" Estilo Pixar 3D, ultra detallado, calidad 4K, iluminación cinematográfica, formato vertical 9:16 para redes sociales.";
const finalPrompt = `${prompt.trim()}. ${styleSuffix}`;
// Construimos las "parts" según el modo
const parts = [{ text: finalPrompt }];
if (mode === "edit" && base64ImageData && imageFile?.type) {
parts.push({
inlineData: {
mimeType: imageFile.type, // "image/jpeg" o "image/png"
data: base64ImageData,
},
});
}
const payload = {
contents: [
{
parts,
},
],
generationConfig: {
// Solo queremos la imagen de vuelta
responseModalities: ["IMAGE"],
// Forzamos aspecto vertical (9:16)
imageConfig: {
aspectRatio: "9:16",
},
},
};
try {
let response = await fetch(apiUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorBody = await response.json().catch(() => ({}));
throw new Error(
`Error de la API: ${response.status} - ${
errorBody.error?.message || "Error desconocido"
}`
);
}
const result = await response.json();
const base64Data =
result?.candidates?.[0]?.content?.parts?.find(
(p) => p.inlineData
)?.inlineData?.data;
if (base64Data) {
const imageUrl = `data:image/png;base64,${base64Data}`;
setGeneratedImage(imageUrl);
} else {
// A veces la respuesta puede ser texto (por ejemplo, si la solicitud no es segura)
const textResponse =
result?.candidates?.[0]?.content?.parts?.[0]?.text;
if (textResponse) {
setError(
`La API devolvió un mensaje de texto en lugar de una imagen: ${textResponse}`
);
} else {
setError(
"No se pudo generar la imagen. La respuesta de la API no contenÃa datos de imagen."
);
console.error("Respuesta inesperada de la API:", result);
}
}
} catch (err) {
setError(`Error al generar la imagen: ${err.message}`);
console.error(err);
} finally {
setLoading(false);
}
};
return (
Editor de Imagen AI (Pixar 3D · 4K · Vertical)
{/* Selector de modo */}
{/* Paso 1: Subir Imagen (solo relevante para modo edit) */}
Paso 1: Sube tu imagen (solo si vas a EDITAR)
{/* Previsualización de Imagen original */}
{mode === "edit" && imageFile && base64ImageData && !generatedImage && (
)}
{/* Paso 2: Escribir Instrucción */}
Paso 2: Escribe tu instrucción
{/* Paso 3: Generar */}
{loading ? (
<>
Generando...
>
) : (
(mode === "edit" ? "Editar imagen" : "Crear imagen desde cero")
)}
{/* Mensaje de Error */}
{error && (
Error:
{error}
)}
{/* Imagen Generada */}
{generatedImage && !loading && (
Resultado (Pixar 3D · 4K · Vertical):
)}
);
}