vidcraft / src /pages /logos.tsx
tsi-org's picture
Update src/pages/logos.tsx
9561fcb
import React, { useState, useEffect, FormEvent, ChangeEvent } from 'react';
interface InputData {
prompt: string;
negative_prompt: string;
image: File | null;
mask: File | null;
width: number;
height: number;
num_outputs: number;
scheduler: string;
num_inference_steps: number;
guidance_scale: number;
prompt_strength: number;
seed: string;
refine: string;
high_noise_frac: number;
refine_steps: string;
apply_watermark: boolean;
lora_scale: number;
}
interface Prediction {
output: string[];
}
interface OutputData {
prediction: Prediction | null;
timestamp: string;
prompt: string;
}
const TruncatedText = ({ text }: { text: string }) => {
const [isTruncated, setIsTruncated] = useState(true);
const toggleTruncated = () => {
setIsTruncated(!isTruncated);
};
if (text.length < 34) {
return <span>{text}</span>;
}
return (
<span>
{isTruncated ? `${text.substring(0, 24)}...` : text}
<button onClick={toggleTruncated}>
{isTruncated ? 'Read More' : 'Read Less'}
</button>
</span>
);
};
const IndexPage = () => {
const [inputData, setInputData] = useState<InputData>({
prompt: '',
negative_prompt: '',
image: null,
mask: null,
width: 1024,
height: 1024,
num_outputs: 1,
scheduler: 'K_EULER',
num_inference_steps: 50,
guidance_scale: 7.5,
prompt_strength: 0.8,
seed: '',
refine: 'no_refiner',
high_noise_frac: 0.8,
refine_steps: '',
apply_watermark: false,
lora_scale: 0.6,
});
const [output, setOutput] = useState<OutputData[]>([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const savedOutput = localStorage.getItem('output');
if (savedOutput) {
setOutput(JSON.parse(savedOutput));
}
}, []);
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value, type } = e.target as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;
// Handle file inputs differently
if (type === 'file') {
const files = (e.target as HTMLInputElement).files;
setInputData((prevData) => ({ ...prevData, [name]: files ? files[0] : null }));
} else if (type === 'checkbox') {
// Check if the 'checked' property exists before accessing it
if ('checked' in e.target) {
setInputData((prevData) => ({ ...prevData, [name]: (e.target as HTMLInputElement).checked }));
}
} else {
setInputData((prevData) => ({ ...prevData, [name]: value }));
}
};
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
setIsLoading(true);
try {
const formData = new FormData();
for (const key in inputData) {
// Changed this line to fix the 'no-prototype-builtins' error
if (Object.prototype.hasOwnProperty.call(inputData, key)) {
const inputKey = key as keyof InputData;
if (typeof inputData[inputKey] === 'string' || inputData[inputKey] instanceof Blob) {
formData.append(inputKey, inputData[inputKey] as string | Blob);
}
}
}
const response = await fetch('/api/replicate_logo', {
method: 'POST',
body: formData,
});
const result = await response.json();
if (response.ok) {
const timestamp = new Date().toISOString();
const newOutput = {
prediction: result.prediction,
timestamp,
prompt: inputData.prompt,
};
const updatedOutput = [...output, newOutput];
setOutput(updatedOutput);
localStorage.setItem('output', JSON.stringify(updatedOutput));
setInputData({
prompt: '',
negative_prompt: '',
image: null,
mask: null,
width: 1024,
height: 1024,
num_outputs: 1,
scheduler: 'K_EULER',
num_inference_steps: 50,
guidance_scale: 7.5,
prompt_strength: 0.8,
seed: '',
refine: 'no_refiner',
high_noise_frac: 0.8,
refine_steps: '',
apply_watermark: false,
lora_scale: 0.6,
});
}
} catch (error) {
console.error("An error occurred:", error);
} finally {
setIsLoading(false);
}
};
return (
<div className="bg-gray-900 text-white p-4 container mx-auto" style={{ minHeight: '100vh' }}>
<h1 className="text-2xl mb-4">Image Generation App</h1>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Textareas take full width */}
<div className="col-span-full">
<textarea
name="prompt"
value={inputData.prompt}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full h-24"
placeholder="Input prompt"
></textarea>
</div>
<div className="col-span-full">
<textarea
name="negative_prompt"
value={inputData.negative_prompt}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full h-24"
placeholder="Input Negative Prompt"
></textarea>
</div>
{/* Single row, multiple items */}
<div>
<input
type="file"
name="image"
accept="image/*"
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
/>
</div>
<div>
<input
type="file"
name="mask"
accept="image/*"
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
/>
</div>
<div>
<input
type="number"
name="width"
value={inputData.width}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="Width"
/>
</div>
<div>
<input
type="number"
name="height"
value={inputData.height}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="Height"
/>
</div>
<div>
<input
type="number"
name="num_outputs"
value={inputData.num_outputs}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="Number of Outputs"
/>
</div>
<div>
<input
type="number"
name="num_inference_steps"
value={inputData.num_inference_steps}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="Number of Inference Steps"
/>
</div>
<div>
<input
type="number"
name="guidance_scale"
value={inputData.guidance_scale}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="Guidance Scale"
/>
</div>
<div>
<input
type="number"
name="prompt_strength"
value={inputData.prompt_strength}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="Prompt Strength"
/>
</div>
<div>
<input
type="text"
name="seed"
value={inputData.seed}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="Seed"
/>
</div>
<div className="col-span-full">
<select
id="refine"
name="refine"
value={inputData.refine}
onChange={handleChange}
className="w-full p-2 bg-gray-800 rounded border border-gray-600 disabled:cursor-not-allowed disabled:opacity-50 text-white"
>
<option value="no_refiner">no_refiner</option>
<option value="expert_ensemble_refiner">expert_ensemble_refiner</option>
<option value="base_image_refiner">base_image_refiner</option>
</select>
</div>
<div>
<input
type="number"
name="high_noise_frac"
value={inputData.high_noise_frac}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="High Noise Fraction"
/>
</div>
<div className="col-span-full">
<label className="flex items-center">
<input
type="checkbox"
name="apply_watermark"
checked={inputData.apply_watermark}
onChange={handleChange}
className="mr-2"
/>
Apply Watermark
</label>
</div>
<div>
<input
type="number"
name="lora_scale"
value={inputData.lora_scale}
onChange={handleChange}
className="p-2 bg-gray-800 rounded border border-gray-600 w-full"
placeholder="LoRA Scale"
/>
</div>
</div>
{/* Submit button */}
<button
type="submit"
className={`p-2 px-5 rounded ${isLoading ? 'bg-gray-600' : 'bg-green-600'} text-white`}
disabled={isLoading}
style={{ cursor: isLoading ? 'not-allowed' : 'pointer' }}
>
{isLoading ? 'Generating...' : 'Generate'}
</button>
</form>
<div className="grid lg:grid-cols-3 grid-cols-1 gap-4 mt-8">
{output.map((imageData, index) => (
<div key={index} className="space-y-2">
<h2 className="text-xl">Generated Image {index + 1}:</h2>
{imageData.prediction?.output ? (
<div className="relative">
<img src={imageData.prediction.output[0]} alt={`Generated Image ${index + 1}`} />
<button
onClick={async () => {
try {
const response = await fetch(imageData.prediction?.output[0] || ""); // Used optional chaining and default to empty string
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `generated_image_${index + 1}.png`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (error) {
console.error("Failed to download the image", error);
}
}}
className="absolute top-0 right-0 bg-green-600 text-white px-3 py-1 rounded"
>
Download
</button>
</div>
) : (
<p>Image URLs not available.</p>
)}
<p>Date: {new Date(imageData.timestamp).toLocaleDateString()} Time: {new Date(imageData.timestamp).toLocaleTimeString()}</p>
<TruncatedText text={imageData.prompt} />
</div>
))}
</div>
</div>
)}
export default IndexPage;