import { useEffect, useMemo, useState } from "react";
import Header from "./components/Header";
import UploadZone from "./components/UploadZone";
import ImagePreview from "./components/ImagePreview";
import CaptionResult from "./components/CaptionResult";
import ErrorBanner from "./components/ErrorBanner";
import Spinner from "./components/Spinner";
import { captionImage } from "./services/api";
export default function App() {
const [file, setFile] = useState(null);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const previewUrl = useMemo(
() => (file ? URL.createObjectURL(file) : null),
[file],
);
useEffect(() => {
if (!previewUrl) return;
return () => URL.revokeObjectURL(previewUrl);
}, [previewUrl]);
const handleFileSelected = (nextFile, validationError) => {
setResult(null);
if (validationError) {
setFile(null);
setError(validationError);
return;
}
setError(null);
setFile(nextFile);
};
const handleClear = () => {
setFile(null);
setResult(null);
setError(null);
};
const handleGenerate = async () => {
if (!file || loading) return;
setLoading(true);
setError(null);
setResult(null);
try {
const data = await captionImage(file);
setResult(data);
} catch (err) {
setError(err?.message || "Caption generation failed.");
} finally {
setLoading(false);
}
};
return (
Describe any image{" "}
in natural language
Upload a photo and let the model generate a concise caption.
Powered by a vision-encoder/text-decoder pipeline served over
FastAPI.
{file ? (
) : (
)}
setError(null)} />
Inference
{loading && (
running…
)}
{file
? "Click generate to send the image to the inference endpoint."
: "Upload an image to enable generation."}
{result &&
}
);
}