BennyKok's picture
ok
916c5da
raw
history blame
7.51 kB
"use client";
import { LoadingIcon } from "@/components/LoadingIcon";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
import {
checkStatus,
generate,
generate_img,
getUploadUrl,
} from "@/server/generate";
import { useEffect, useState } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
export default function Page() {
return (
<main className="flex min-h-screen flex-col items-center justify-between mt-10">
<Tabs defaultValue="txt2img" className="w-full max-w-[500px]">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="txt2img">txt2img</TabsTrigger>
<TabsTrigger value="img2img">img2img</TabsTrigger>
</TabsList>
<TabsContent value="txt2img">
<Txt2img />
</TabsContent>
<TabsContent value="img2img">
<Img2img />
</TabsContent>
</Tabs>
</main>
);
}
export function Txt2img() {
const [prompt, setPrompt] = useState("");
const [image, setImage] = useState("");
const [loading, setLoading] = useState(false);
const [runId, setRunId] = useState("");
const [status, setStatus] = useState<string>();
// Polling in frontend to check for the
useEffect(() => {
if (!runId) return;
const interval = setInterval(() => {
checkStatus(runId).then((res) => {
if (res) setStatus(res.status);
if (res && res.status === "success") {
console.log(res.outputs[0]?.data);
setImage(res.outputs[0]?.data?.images[0].url);
setLoading(false);
clearInterval(interval);
}
});
}, 2000);
return () => clearInterval(interval);
}, [runId]);
return (
<Card className="w-full max-w-[500px]">
<CardHeader>
Comfy Deploy - Vector Line Art Tool
<div className="text-xs text-foreground opacity-50">
Lora -{" "}
<a href="https://civitai.com/models/256144/stick-line-vector-illustration">
stick-line-vector-illustration
</a>
</div>
</CardHeader>
<CardContent>
<form
className="grid w-full items-center gap-1.5"
onSubmit={(e) => {
if (loading) return;
e.preventDefault();
setLoading(true);
generate(prompt).then((res) => {
console.log(res);
if (!res) {
setStatus("error");
setLoading(false);
return;
}
setRunId(res.run_id);
});
setStatus("preparing");
}}
>
<Label htmlFor="picture">Image prompt</Label>
<Input
id="picture"
type="text"
value={prompt}
onChange={(e) => setPrompt(e.target.value)}
/>
<Button type="submit" className="flex gap-2" disabled={loading}>
Generate {loading && <LoadingIcon />}
</Button>
<div className="border border-gray-200 w-full square h-[400px] rounded-lg relative">
{!loading && image && (
<img
className="w-full h-full object-contain"
src={image}
alt="Generated image"
></img>
)}
{!image && status && (
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
{status} <LoadingIcon />
</div>
)}
{loading && <Skeleton className="w-full h-full" />}
</div>
</form>
</CardContent>
</Card>
);
}
export function Img2img() {
const [prompt, setPrompt] = useState<File>();
const [image, setImage] = useState("");
const [loading, setLoading] = useState(false);
const [runId, setRunId] = useState("");
const [status, setStatus] = useState<string>();
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setPrompt(e.target.files[0]);
};
// Polling in frontend to check for the
useEffect(() => {
if (!runId) return;
const interval = setInterval(() => {
checkStatus(runId).then((res) => {
if (res) setStatus(res.status);
if (res && res.status === "success") {
console.log(res.outputs[0]?.data);
setImage(res.outputs[0]?.data?.images[0].url);
setLoading(false);
clearInterval(interval);
}
});
}, 2000);
return () => clearInterval(interval);
}, [runId]);
return (
<Card className="w-full max-w-[500px]">
<CardHeader>
Comfy Deploy - Vector Line Art Tool
<div className="text-xs text-foreground opacity-50">
Lora -{" "}
<a href="https://civitai.com/models/256144/stick-line-vector-illustration">
stick-line-vector-illustration
</a>
</div>
</CardHeader>
<CardContent>
<form
className="grid w-full items-center gap-1.5"
onSubmit={(e) => {
e.preventDefault()
if (loading) return;
if (!prompt) return;
setImage("");
setStatus("getting url for upload");
console.log(prompt?.type, prompt?.size);
getUploadUrl(prompt?.type, prompt?.size).then((res) => {
if (!res) return;
setStatus("uploading input");
console.log(res);
fetch(res.upload_url, {
method: "PUT",
body: prompt,
headers: {
"Content-Type": prompt.type,
"x-amz-acl": "public-read",
"Content-Length": prompt.size.toString(),
},
}).then((_res) => {
if (_res.ok) {
setStatus("uploaded input");
setLoading(true);
generate_img(res.download_url).then((res) => {
console.log(res);
if (!res) {
setStatus("error");
setLoading(false);
return;
}
setRunId(res.run_id);
});
setStatus("preparing");
}
});
});
}}
>
<Label htmlFor="picture">Image prompt</Label>
<Input id="picture" type="file" onChange={handleFileChange} />
<Button type="submit" className="flex gap-2" disabled={loading}>
Generate {loading && <LoadingIcon />}
</Button>
<div className="border border-gray-200 w-full square h-[400px] rounded-lg relative">
{!loading && image && (
<img
className="w-full h-full object-contain"
src={image}
alt="Generated image"
></img>
)}
{!image && status && (
<div className="absolute top-0 left-0 w-full h-full flex items-center justify-center gap-2">
{status} <LoadingIcon />
</div>
)}
{loading && <Skeleton className="w-full h-full" />}
</div>
</form>
</CardContent>
</Card>
);
}