forked from innovacion/playground
116 lines
3.7 KiB
TypeScript
116 lines
3.7 KiB
TypeScript
'use client';
|
|
|
|
import { aspectRatioSizes, isValidAspectRatio } from '@/lib/google-ai';
|
|
|
|
interface ImageConfigProps {
|
|
model: string;
|
|
aspectRatio: string;
|
|
onAspectRatioChange: (ratio: string) => void;
|
|
numberOfImages: number;
|
|
onNumberOfImagesChange: (num: number) => void;
|
|
negativePrompt: string;
|
|
onNegativePromptChange: (prompt: string) => void;
|
|
safetyLevel: 'block_none' | 'block_some' | 'block_most';
|
|
onSafetyLevelChange: (level: 'block_none' | 'block_some' | 'block_most') => void;
|
|
}
|
|
|
|
export function ImageConfig({
|
|
model,
|
|
aspectRatio,
|
|
onAspectRatioChange,
|
|
numberOfImages,
|
|
onNumberOfImagesChange,
|
|
negativePrompt,
|
|
onNegativePromptChange,
|
|
safetyLevel,
|
|
onSafetyLevelChange,
|
|
}: ImageConfigProps) {
|
|
const availableRatios = Object.keys(aspectRatioSizes);
|
|
const size = aspectRatioSizes[aspectRatio];
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<h3 className="text-sm font-medium text-foreground">Configuración de Imagen</h3>
|
|
|
|
{/* Aspect Ratio */}
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-medium text-muted-foreground">
|
|
Aspect Ratio
|
|
</label>
|
|
<div className="grid grid-cols-5 gap-2">
|
|
{availableRatios.map((ratio) => (
|
|
<button
|
|
key={ratio}
|
|
onClick={() => onAspectRatioChange(ratio)}
|
|
disabled={!isValidAspectRatio(model, ratio) && !model.startsWith('gemini')}
|
|
className={`
|
|
px-3 py-2 text-xs rounded-lg font-medium transition-all
|
|
${aspectRatio === ratio
|
|
? 'bg-primary text-primary-foreground'
|
|
: 'bg-secondary hover:bg-accent'
|
|
}
|
|
${!isValidAspectRatio(model, ratio) && !model.startsWith('gemini')
|
|
? 'opacity-50 cursor-not-allowed'
|
|
: ''
|
|
}
|
|
`}
|
|
>
|
|
{ratio}
|
|
</button>
|
|
))}
|
|
</div>
|
|
{size && (
|
|
<p className="text-xs text-muted-foreground">
|
|
Dimensiones: {size.width}x{size.height}px
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* Número de imágenes */}
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-medium text-muted-foreground">
|
|
Número de imágenes: {numberOfImages}
|
|
</label>
|
|
<input
|
|
type="range"
|
|
min="1"
|
|
max="4"
|
|
value={numberOfImages}
|
|
onChange={(e) => onNumberOfImagesChange(parseInt(e.target.value))}
|
|
className="w-full"
|
|
/>
|
|
</div>
|
|
|
|
{/* Negative Prompt */}
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-medium text-muted-foreground">
|
|
Negative Prompt (opcional)
|
|
</label>
|
|
<textarea
|
|
value={negativePrompt}
|
|
onChange={(e) => onNegativePromptChange(e.target.value)}
|
|
placeholder="Elementos que NO quieres en la imagen..."
|
|
className="w-full px-3 py-2 bg-secondary border border-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary resize-none"
|
|
rows={2}
|
|
/>
|
|
</div>
|
|
|
|
{/* Safety Level */}
|
|
<div className="space-y-2">
|
|
<label className="text-xs font-medium text-muted-foreground">
|
|
Nivel de Seguridad
|
|
</label>
|
|
<select
|
|
value={safetyLevel}
|
|
onChange={(e) => onSafetyLevelChange(e.target.value as any)}
|
|
className="w-full px-3 py-2 bg-secondary border border-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
|
>
|
|
<option value="block_none">Sin bloqueo</option>
|
|
<option value="block_some">Bloqueo moderado</option>
|
|
<option value="block_most">Bloqueo alto</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|