|
function Spinner({ config }) { |
|
const ref = React.useRef(null); |
|
|
|
React.useEffect(() => { |
|
const spinner = new Spin.Spinner({ |
|
lines: 13, |
|
color: "#ffffff", |
|
...config, |
|
}); |
|
spinner.spin(ref.current); |
|
return () => spinner.stop(); |
|
}, [ref]); |
|
|
|
return <span ref={ref} />; |
|
} |
|
|
|
function Result({ callId, selectedFile }) { |
|
const [result, setResult] = React.useState(); |
|
const [intervalId, setIntervalId] = React.useState(); |
|
|
|
React.useEffect(() => { |
|
if (result) { |
|
clearInterval(intervalId); |
|
return; |
|
} |
|
|
|
const _intervalID = setInterval(async () => { |
|
const resp = await fetch(`/result/${callId}`); |
|
if (resp.status === 200) { |
|
setResult(await resp.json()); |
|
} |
|
}, 100); |
|
|
|
setIntervalId(_intervalID); |
|
|
|
return () => clearInterval(intervalId); |
|
}, [result]); |
|
|
|
return ( |
|
<div class="flex items-center content-center justify-center space-x-4 "> |
|
<img src={URL.createObjectURL(selectedFile)} class="h-[300px]" /> |
|
{!result && <Spinner config={{}} />} |
|
{result && ( |
|
<p class="w-[200px] p-4 bg-zinc-200 rounded-lg whitespace-pre-wrap text-xs font-mono"> |
|
{JSON.stringify(result, undefined, 1)} |
|
</p> |
|
)} |
|
</div> |
|
); |
|
} |
|
|
|
function Form({ onSubmit, onFileSelect, selectedFile }) { |
|
return ( |
|
<form class="flex flex-col space-y-4 items-center"> |
|
<div class="text-2xl font-semibold text-gray-700"> ViT-GPT2 Image Captioning </div> |
|
<input |
|
accept="image/*" |
|
type="file" |
|
name="file" |
|
onChange={onFileSelect} |
|
class="block w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 cursor-pointer" |
|
/> |
|
{selectedFile ? ( |
|
<img src={URL.createObjectURL(selectedFile)} class="h-[300px]" /> |
|
) : null} |
|
<div> |
|
<button |
|
type="button" |
|
onClick={onSubmit} |
|
disabled={!selectedFile} |
|
class="bg-indigo-400 disabled:bg-zinc-500 hover:bg-indigo-600 text-white font-bold py-2 px-4 rounded text-sm" |
|
> |
|
Upload |
|
</button> |
|
</div> |
|
</form> |
|
); |
|
} |
|
|
|
function App() { |
|
const [selectedFile, setSelectedFile] = React.useState(); |
|
const [callId, setCallId] = React.useState(); |
|
|
|
const handleSubmission = async () => { |
|
const formData = new FormData(); |
|
formData.append("image", selectedFile); |
|
|
|
const resp = await fetch("/parse", { |
|
method: "POST", |
|
body: formData, |
|
}); |
|
|
|
if (resp.status !== 200) { |
|
throw new Error("An error occurred: " + resp.status); |
|
} |
|
const body = await resp.json(); |
|
setCallId(body.call_id); |
|
}; |
|
|
|
return ( |
|
<div class="absolute inset-0 bg-gradient-to-r from-indigo-300 via-purple-300 to-pink-300"> |
|
<div class="mx-auto max-w-md py-8"> |
|
<main class="rounded-xl bg-white p-6"> |
|
{!callId && ( |
|
<Form |
|
onSubmit={handleSubmission} |
|
onFileSelect={(e) => setSelectedFile(e.target.files[0])} |
|
selectedFile={selectedFile} |
|
/> |
|
)} |
|
{callId && <Result callId={callId} selectedFile={selectedFile} />} |
|
</main> |
|
</div> |
|
</div> |
|
); |
|
} |
|
|
|
const container = document.getElementById("react"); |
|
ReactDOM.createRoot(container).render(<App />); |
|
|