diff --git a/app/page.tsx b/app/page.tsx index f864277..76a966b 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -10,32 +10,50 @@ import { ModelIcon } from "@/components/icons/model-icon"; import { cn } from "@/lib/utils"; import ModelCompare from "@/components/model-compare"; import OriginalCompare from "@/components/original-compare"; +import { + type Model, + ModelDropdown, + UPSCALE_MODELS, +} from "@/components/model-dropdown"; +import { Button } from "@/components/ui/button"; fal.config({ proxyUrl: "/api/proxy" }); interface ModelResult { - name: string; image: string; inferenceTime: number; } type CompareMode = "original" | "model"; +UPSCALE_MODELS; + export default function Lightning() { const [mode, setMode] = useState("original"); const [position, setPosition] = useState(50); const [originalImage, setOriginalImage] = useState(null); + const [imageFile, setImageFile] = useState(null); + + const [firstModelLoading, setFirstModelLoading] = useState(false); + const [secondModelLoading, setSecondModelLoading] = useState(false); + const [firstModel, setFirstModel] = useState(UPSCALE_MODELS[0]); + const [secondModel, setSecondModel] = useState( + UPSCALE_MODELS[1] + ); + const [firstModelOutput, setFirstModelOutput] = useState( + null + ); + const [secondModelOutput, setSecondModelOutput] = + useState(null); - const [modelOne, setModelOne] = useState(null); - const [modelOneLoading, setModelOneLoading] = useState(false); - const [modelTwo, setModelTwo] = useState(null); - const [modelTwoLoading, setModelTwoLoading] = useState(false); + const upscaleWithFirstModel = async (file: File) => { + if (!firstModel) return; + + setFirstModelLoading(true); - const upscaleWithModelOne = async (file: File) => { let inferenceTime; - setModelOneLoading(true); - const result: Record = await fal.subscribe("fal-ai/ccsr", { + const result: Record = await fal.subscribe(firstModel.model, { input: { image_url: file, }, @@ -48,22 +66,23 @@ export default function Lightning() { }); if (result) { - setModelOne({ - name: "CCSR", + setFirstModelOutput({ image: result.image.url as string, inferenceTime, }); } - setModelOneLoading(false); + setFirstModelLoading(false); }; - const upscaleWithModelTwo = async (file: File) => { - let inferenceTime; + const upscaleWithSecondModel = async (file: File) => { + if (!secondModel) return; + + setSecondModelLoading(true); - setModelTwoLoading(true); + let inferenceTime; - const result: Record = await fal.subscribe("fal-ai/supir", { + const result: Record = await fal.subscribe(secondModel.model, { input: { image_url: file, }, @@ -76,25 +95,28 @@ export default function Lightning() { }); if (result) { - setModelTwo({ - name: "SUPIR", + setSecondModelOutput({ image: result.image.url as string, inferenceTime, }); } - setModelTwoLoading(false); + setSecondModelLoading(false); }; - const handleOnChange = async (file: File) => { - const blobUrl = URL.createObjectURL(file); + const handleCompare = async () => { + if (!imageFile) return; + + setPosition(50); + + const blobUrl = URL.createObjectURL(imageFile); setOriginalImage(blobUrl); - setModelOne(null); - setModelTwo(null); + setFirstModelOutput(null); + setSecondModelOutput(null); - upscaleWithModelOne(file); - upscaleWithModelTwo(file); + upscaleWithFirstModel(imageFile); + upscaleWithSecondModel(imageFile); }; useEffect(() => { @@ -106,7 +128,7 @@ export default function Lightning() { return (
-
+
@@ -116,11 +138,12 @@ export default function Lightning() { { - if (e.target?.files?.[0]) { - handleOnChange(e.target?.files?.[0]); - } + setImageFile(e.target?.files?.[0] || null); }} - className="font-light mx-auto rounded-full h-10 pr-10 truncate" + className={cn( + "font-light mx-auto rounded-full h-10 pr-10 truncate", + !imageFile && "border-orange-400" + )} placeholder="Type something..." />
@@ -153,13 +176,34 @@ export default function Lightning() {
+
+
+ setFirstModel(model)} + value={firstModel} + /> +
+
+ +
+
+ setSecondModel(model)} + value={secondModel} + /> +
+
{mode === "model" && ( )} {mode === "original" && ( @@ -167,8 +211,10 @@ export default function Lightning() { originalImage={originalImage} position={position} setPosition={setPosition} - modelOne={modelOne} - modelTwo={modelTwo} + firstModel={firstModel} + firstModelOutput={firstModelOutput} + secondModel={secondModel} + secondModelOutput={secondModelOutput} /> )}
diff --git a/components/model-compare.tsx b/components/model-compare.tsx index 9f93118..22a4fd2 100644 --- a/components/model-compare.tsx +++ b/components/model-compare.tsx @@ -10,52 +10,24 @@ interface ModelCompareProps { originalImage: string | null; position: number; setPosition: (position: number) => void; - modelOne: any; - modelTwo: any; + firstModel: any; + firstModelOutput: any; + secondModel: any; + secondModelOutput: any; } const ModelCompare = ({ originalImage, position, setPosition, - modelOne, - modelTwo, + firstModel, + firstModelOutput, + secondModel, + secondModelOutput, }: ModelCompareProps) => { return ( -
+
- {originalImage ? (
- + } itemTwo={ <> @@ -102,6 +81,40 @@ const ModelCompare = ({ />
)} + {firstModel && secondModel && ( + + )}
); diff --git a/components/model-dropdown.tsx b/components/model-dropdown.tsx new file mode 100644 index 0000000..91a3e6e --- /dev/null +++ b/components/model-dropdown.tsx @@ -0,0 +1,72 @@ +"use client"; + +import * as React from "react"; +import { ChevronDown } from "lucide-react"; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Button } from "@/components/ui/button"; + +export interface Model { + shortname: string; + name: string; + model: string; + link: string; +} + +export const UPSCALE_MODELS: Model[] = [ + { + shortname: "CCSR", + name: "CCSR Upscaler", + model: "fal-ai/ccsr", + link: "https://fal.ai/models/ccsr", + }, + { + shortname: "SUPIR", + name: "SUPIR Upscaler", + model: "fal-ai/supir", + link: "https://fal.ai/models/supir", + }, + { + shortname: "CREATIVE", + name: "Creative Upscaler", + model: "fal-ai/creative-upscaler", + link: "https://fal.ai/models/creative-upscaler", + }, + { + shortname: "ESRGAN", + name: "ESRGAN Upscaler", + model: "fal-ai/esrgan", + link: "https://fal.run/fal-ai/esrgan", + }, +]; + +export function ModelDropdown({ + onSelect, + value, +}: { + value: Model | null; + onSelect: (model: Model) => void; +}) { + return ( + + + + + + {UPSCALE_MODELS.map((model) => ( + onSelect(model)}> + {model.name} + + ))} + + + ); +} diff --git a/components/original-compare.tsx b/components/original-compare.tsx index eec9fce..250ed10 100644 --- a/components/original-compare.tsx +++ b/components/original-compare.tsx @@ -10,35 +10,24 @@ interface OriginalCompareProps { originalImage: string | null; position: number; setPosition: (position: number) => void; - modelOne: any; - modelTwo: any; + firstModel: any; + firstModelOutput: any; + secondModel: any; + secondModelOutput: any; } const OriginalCompare = ({ originalImage, position, setPosition, - modelOne, - modelTwo, + firstModel, + firstModelOutput, + secondModel, + secondModelOutput, }: OriginalCompareProps) => { return ( -
+
- {originalImage ? (
@@ -86,24 +77,27 @@ const OriginalCompare = ({ />
)} + + {firstModel && ( + + )}
- {originalImage ? (
} @@ -151,6 +147,23 @@ const OriginalCompare = ({ />
)} + {secondModel && ( + + )}
);