Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
"use client" | |
import { useEffect, useRef, useState, useTransition } from "react" | |
// import AutoSizer from "react-virtualized-auto-sizer" | |
import { RenderedScene } from "@/types" | |
import { getRender, newRender } from "@/app/engine/render" | |
import { useStore } from "@/app/store" | |
import { cn } from "@/lib/utils" | |
import { getInitialRenderedScene } from "@/lib/getInitialRenderedScene" | |
import { Progress } from "@/app/interface/progress" | |
import { see } from "@/app/engine/caption" | |
// import { Bubble } from "./bubble" | |
export function Panel({ | |
panel, | |
className = "", | |
width = 1, | |
height = 1, | |
delay = 0, | |
}: { | |
panel: number | |
className?: string | |
width?: number | |
height?: number | |
delay?: number | |
}) { | |
const font = useStore(state => state.font) | |
const preset = useStore(state => state.preset) | |
const setGeneratingImages = useStore(state => state.setGeneratingImages) | |
const panelGenerationStatus = useStore(state => state.panelGenerationStatus) | |
const isLoading = panelGenerationStatus[panel] || false | |
const panels = useStore(state => state.panels) | |
const prompt = panels[panel] || "" | |
const zoomLevel = useStore(state => state.zoomLevel) | |
// const setCaption = useStore(state => state.setCaption) | |
// const captions = useStore(state => state.captions) | |
// const caption = captions[panel] || "" | |
const [_isPending, startTransition] = useTransition() | |
const [rendered, setRendered] = useState<RenderedScene>(getInitialRenderedScene()) | |
const renderedRef = useRef<RenderedScene>() | |
const timeoutRef = useRef<any>(null) | |
// since this run in its own loop, we need to use references everywhere | |
// but perhaps this could be refactored | |
useEffect(() => { | |
startTransition(async () => { | |
// console.log("Panel prompt: "+ prompt) | |
if (!prompt?.length) { return } | |
if (isLoading) { return } | |
console.log("Loading panel..") | |
// console.log("calling:\nconst newRendered = await newRender({ prompt, preset, width, height })") | |
console.log({ | |
panel, prompt, width, height | |
}) | |
console.log("") | |
// important: update the status, and clear the scene | |
setGeneratingImages(panel, true) | |
setRendered(getInitialRenderedScene()) | |
const newRendered = await newRender({ prompt, width, height }) | |
if (newRendered) { | |
// console.log("newRendered:", newRendered) | |
setRendered(renderedRef.current = newRendered) | |
// but we are still loading! | |
} else { | |
setRendered(renderedRef.current = { | |
renderId: "", | |
status: "error", | |
assetUrl: "", | |
alt: "", | |
maskUrl: "", | |
error: "failed to fetch the data", | |
segments: [] | |
}) | |
setGeneratingImages(panel, false) | |
return | |
} | |
}) | |
}, [prompt, font, width, height]) | |
const checkStatus = () => { | |
startTransition(async () => { | |
clearTimeout(timeoutRef.current) | |
if (!renderedRef.current?.renderId || renderedRef.current?.status !== "pending") { | |
timeoutRef.current = setTimeout(checkStatus, 1000) | |
return | |
} | |
try { | |
setGeneratingImages(panel, true) | |
// console.log(`Checking job status API for job ${renderedRef.current?.renderId}`) | |
const newRendered = await getRender(renderedRef.current.renderId) | |
// console.log("got a response!", newRendered) | |
if (JSON.stringify(renderedRef.current) !== JSON.stringify(newRendered)) { | |
console.log("updated panel:", newRendered) | |
setRendered(renderedRef.current = newRendered) | |
setGeneratingImages(panel, true) | |
} | |
// console.log("status:", newRendered.status) | |
if (newRendered.status === "pending") { | |
// console.log("job not finished") | |
timeoutRef.current = setTimeout(checkStatus, 1000) | |
} else { | |
console.log("panel finished!") | |
setGeneratingImages(panel, false) | |
} | |
} catch (err) { | |
console.error(err) | |
timeoutRef.current = setTimeout(checkStatus, 1000) | |
} | |
}) | |
} | |
useEffect(() => { | |
console.log("starting timeout") | |
// normally it should reply in < 1sec, but we could also use an interval | |
timeoutRef.current = setTimeout(checkStatus, delay) | |
return () => { | |
clearTimeout(timeoutRef.current) | |
} | |
}, []) | |
/* | |
doing the captionning from the browser is expensive | |
a simpler solution is to caption directly during SDXL generation | |
useEffect(() => { | |
if (!rendered.assetUrl) { return } | |
// the asset url can evolve with time (link to a better resolution image) | |
// however it would be costly to ask for the caption, the low resolution is enough for the semantic resolution | |
// so we just do nothing if we already have the caption | |
if (caption) { return } | |
startTransition(async () => { | |
try { | |
const newCaption = await see({ | |
prompt: "please caption the following image", | |
imageBase64: rendered.assetUrl | |
}) | |
if (newCaption) { | |
setCaption(newCaption) | |
} | |
} catch (err) { | |
console.error(`failed to generate the caption:`, err) | |
} | |
}) | |
}, [rendered.assetUrl, caption]) | |
*/ | |
const frameClassName = cn( | |
`w-full h-full`, | |
`border-stone-900`, | |
`transition-all duration-200 ease-in-out`, | |
zoomLevel > 70 ? `border-2` : zoomLevel > 40 ? `border` : `border border-transparent`, | |
`shadow-sm`, | |
`rounded-sm`, | |
`overflow-hidden`, | |
) | |
if (prompt && !rendered.assetUrl) { | |
return ( | |
<div className={cn( | |
frameClassName, | |
`flex flex-col items-center justify-center`, | |
className | |
)}> | |
<Progress isLoading /> | |
</div> | |
) | |
} | |
return ( | |
<div className={cn( | |
frameClassName, | |
{ "grayscale": preset.color === "grayscale" }, | |
className | |
)}> | |
{rendered.assetUrl && <img | |
src={rendered.assetUrl} | |
className="w-full h-full object-cover" | |
alt={rendered.alt} | |
/>} | |
{/*<Bubble className="absolute top-4 left-4"> | |
Hello, world! | |
</Bubble> | |
*/} | |
</div> | |
) | |
} |