104 lines
4.2 KiB
TypeScript
104 lines
4.2 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
|
import { GenerationResult } from '../types';
|
|
import { DownloadIcon } from './icons/DownloadIcon';
|
|
import { MagicIcon } from './icons/MagicIcon';
|
|
|
|
interface ImageResultProps {
|
|
result: GenerationResult | null;
|
|
onRefine: (refinementPrompt: string) => void;
|
|
masterPrompt: string;
|
|
isLoading: boolean;
|
|
loadingMessage: string;
|
|
onStartOver: () => void;
|
|
}
|
|
|
|
const ImageResult: React.FC<ImageResultProps> = ({ result, onRefine, masterPrompt, isLoading, loadingMessage, onStartOver }) => {
|
|
const [refinementPrompt, setRefinementPrompt] = useState('');
|
|
|
|
const handleRefineSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (refinementPrompt.trim()) {
|
|
onRefine(refinementPrompt);
|
|
setRefinementPrompt('');
|
|
}
|
|
};
|
|
|
|
if (!result) {
|
|
return (
|
|
<div className="text-center">
|
|
<p>Something went wrong. No image was generated.</p>
|
|
<button onClick={onStartOver} className="mt-4 bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded">Start Over</button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="w-full flex flex-col lg:flex-row gap-8">
|
|
<div className="lg:w-2/3 relative">
|
|
<div className="aspect-w-16 aspect-h-9 bg-black rounded-lg overflow-hidden shadow-lg">
|
|
<img src={`data:image/png;base64,${result.currentImage}`} alt="Generated thumbnail" className="w-full h-full object-contain" />
|
|
</div>
|
|
{isLoading && (
|
|
<div className="absolute inset-0 bg-black/70 flex flex-col items-center justify-center text-center p-4 rounded-lg">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-purple-400 mb-4"></div>
|
|
<p className="text-lg text-purple-300">{loadingMessage}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="lg:w-1/3 flex flex-col">
|
|
<h2 className="text-2xl font-bold text-gray-100 mb-4">Your Masterpiece</h2>
|
|
|
|
<div className="flex-grow space-y-6">
|
|
<form onSubmit={handleRefineSubmit}>
|
|
<label htmlFor="refinement-prompt" className="block text-lg font-semibold text-purple-300 mb-2">
|
|
Refine Your Image
|
|
</label>
|
|
<textarea
|
|
id="refinement-prompt"
|
|
value={refinementPrompt}
|
|
onChange={(e) => setRefinementPrompt(e.target.value)}
|
|
placeholder="e.g., 'Make the smile a little softer.' or 'Change the background to be more blurry.'"
|
|
className="w-full bg-gray-700 border border-gray-600 rounded-lg p-3 text-base h-24 focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition"
|
|
required
|
|
/>
|
|
<button
|
|
type="submit"
|
|
disabled={isLoading || !refinementPrompt}
|
|
className="w-full mt-3 bg-purple-600 hover:bg-purple-700 disabled:bg-gray-600 text-white font-bold py-2.5 px-4 rounded-lg transition-colors duration-300 flex items-center justify-center gap-2"
|
|
>
|
|
<MagicIcon className="w-5 h-5"/> Refine
|
|
</button>
|
|
</form>
|
|
|
|
<div>
|
|
<details className="bg-gray-700/50 rounded-lg">
|
|
<summary className="cursor-pointer text-purple-300 font-semibold p-3">View Master Prompt</summary>
|
|
<p className="p-3 pt-0 text-gray-400 text-sm">{masterPrompt}</p>
|
|
</details>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-8 space-y-3">
|
|
<a
|
|
href={`data:image/png;base64,${result.currentImage}`}
|
|
download="kpop-thumbnail.png"
|
|
className="w-full bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-4 rounded-lg transition-colors duration-300 flex items-center justify-center gap-2 text-lg"
|
|
>
|
|
<DownloadIcon className="w-6 h-6" /> Download Image
|
|
</a>
|
|
<button
|
|
onClick={onStartOver}
|
|
className="w-full bg-gray-600 hover:bg-gray-500 text-white font-bold py-2 px-4 rounded-lg transition-colors duration-300"
|
|
>
|
|
Start Over
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ImageResult;
|