|
"use client" |
|
|
|
import { useEffect, useRef, useState, useTransition } from "react" |
|
|
|
|
|
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" |
|
|
|
|
|
|
|
|
|
export function Panel({ |
|
panel, |
|
className = "", |
|
width = 1, |
|
height = 1, |
|
}: { |
|
panel: number |
|
className?: string |
|
width?: number |
|
height?: number |
|
}) { |
|
const panelId = `${panel}` |
|
|
|
const ref = useRef<HTMLImageElement>(null) |
|
const font = useStore(state => state.font) |
|
const preset = useStore(state => state.preset) |
|
|
|
const setGeneratingImages = useStore(state => state.setGeneratingImages) |
|
|
|
const [imageWithText, setImageWithText] = useState("") |
|
const panels = useStore(state => state.panels) |
|
const prompt = panels[panel] || "" |
|
|
|
const captions = useStore(state => state.captions) |
|
const caption = captions[panel] || "" |
|
|
|
const zoomLevel = useStore(state => state.zoomLevel) |
|
const showCaptions = useStore(state => state.showCaptions) |
|
|
|
const addToUpscaleQueue = useStore(state => state.addToUpscaleQueue) |
|
|
|
const [_isPending, startTransition] = useTransition() |
|
const renderedScenes = useStore(state => state.renderedScenes) |
|
const setRendered = useStore(state => state.setRendered) |
|
|
|
const rendered = renderedScenes[panel] || getInitialRenderedScene() |
|
|
|
|
|
const renderedRef = useRef<RenderedScene>() |
|
const renderedKey = JSON.stringify(rendered) |
|
useEffect(() => { renderedRef.current = rendered }, [renderedKey]) |
|
|
|
const timeoutRef = useRef<any>(null) |
|
|
|
const delay = 3000 + (1000 * panel) |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
if (!prompt?.length) { return } |
|
|
|
|
|
setGeneratingImages(panelId, true) |
|
|
|
|
|
setRendered(panelId, getInitialRenderedScene()) |
|
|
|
setTimeout(() => { |
|
startTransition(async () => { |
|
|
|
|
|
|
|
let newRendered: RenderedScene |
|
try { |
|
newRendered = await newRender({ prompt, width, height }) |
|
} catch (err) { |
|
|
|
newRendered = await newRender({ prompt, width, height }) |
|
} |
|
|
|
if (newRendered) { |
|
|
|
setRendered(panelId, newRendered) |
|
|
|
|
|
} else { |
|
setRendered(panelId, { |
|
renderId: "", |
|
status: "pending", |
|
assetUrl: "", |
|
alt: "", |
|
maskUrl: "", |
|
error: "", |
|
segments: [] |
|
}) |
|
setGeneratingImages(panelId, false) |
|
return |
|
} |
|
}) |
|
}, 2000 * panel) |
|
}, [prompt, width, height]) |
|
|
|
|
|
const checkStatus = () => { |
|
startTransition(async () => { |
|
clearTimeout(timeoutRef.current) |
|
|
|
if (!renderedRef.current?.renderId || renderedRef.current?.status !== "pending") { |
|
timeoutRef.current = setTimeout(checkStatus, delay) |
|
return |
|
} |
|
try { |
|
setGeneratingImages(panelId, true) |
|
|
|
const newRendered = await getRender(renderedRef.current.renderId) |
|
|
|
|
|
if (JSON.stringify(renderedRef.current) !== JSON.stringify(newRendered)) { |
|
|
|
setRendered(panelId, renderedRef.current = newRendered) |
|
setGeneratingImages(panelId, true) |
|
} |
|
|
|
|
|
if (newRendered.status === "pending") { |
|
|
|
timeoutRef.current = setTimeout(checkStatus, delay) |
|
} else if (newRendered.status === "error" || |
|
(newRendered.status === "completed" && !newRendered.assetUrl?.length)) { |
|
|
|
try { |
|
const newAttempt = await newRender({ prompt, width, height }) |
|
setRendered(panelId, newAttempt) |
|
} catch (err) { |
|
console.error("yeah sorry, something is wrong.. aborting", err) |
|
setGeneratingImages(panelId, false) |
|
} |
|
} else { |
|
console.log("panel finished!") |
|
setGeneratingImages(panelId, false) |
|
addToUpscaleQueue(panelId, newRendered) |
|
} |
|
} catch (err) { |
|
console.error(err) |
|
timeoutRef.current = setTimeout(checkStatus, delay) |
|
} |
|
}) |
|
} |
|
|
|
useEffect(() => { |
|
|
|
clearTimeout(timeoutRef.current) |
|
|
|
|
|
timeoutRef.current = setTimeout(checkStatus, delay) |
|
|
|
return () => { |
|
clearTimeout(timeoutRef.current) |
|
} |
|
}, [prompt, width, height]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const frameClassName = cn( |
|
|
|
`w-full h-full`, |
|
`border-stone-800`, |
|
`transition-all duration-200 ease-in-out`, |
|
zoomLevel > 140 ? `border-[2px] md:border-[4px] rounded-sm md:rounded-md` : |
|
zoomLevel > 120 ? `border-[1.5px] md:border-[3px] rounded-xs md:rounded-sm` : |
|
zoomLevel > 90 ? `border-[1px] md:border-[2px] rounded-xs md:rounded-sm` : |
|
zoomLevel > 40 ? `border-[0.5px] md:border-[1px] rounded-none md:rounded-xs` : |
|
`border-transparent md:border-[0.5px] rounded-none md:rounded-none`, |
|
`shadow-sm`, |
|
`overflow-hidden`, |
|
`print:border-[1.5px] print:shadow-none`, |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
)}> |
|
<div className={cn( |
|
``, |
|
`bg-stone-50`, |
|
`border-stone-800`, |
|
`transition-all duration-200 ease-in-out`, |
|
zoomLevel > 140 ? `border-b-[2px] md:border-b-[4px]` : |
|
zoomLevel > 120 ? `border-b-[1.5px] md:border-b-[3px]` : |
|
zoomLevel > 90 ? `border-b-[1px] md:border-b-[2px]` : |
|
zoomLevel > 40 ? `border-b-[0.5px] md:border-b-[1px]` : |
|
`border-transparent md:border-b-[0.5px]`, |
|
`print:border-b-[1.5px]`, |
|
`truncate`, |
|
|
|
zoomLevel > 200 ? `p-4 md:p-8` : |
|
zoomLevel > 180 ? `p-[14px] md:p-8` : |
|
zoomLevel > 160 ? `p-[12px] md:p-[28px]` : |
|
zoomLevel > 140 ? `p-[10px] md:p-[26px]` : |
|
zoomLevel > 120 ? `p-2 md:p-6` : |
|
zoomLevel > 100 ? `p-1.5 md:p-[20px]` : |
|
zoomLevel > 90 ? `p-1.5 md:p-4` : |
|
zoomLevel > 40 ? `p-1 md:p-2` : |
|
`p-0.5 md:p-2`, |
|
|
|
zoomLevel > 220 ? `text-xl md:text-4xl` : |
|
zoomLevel > 200 ? `text-lg md:text-3xl` : |
|
zoomLevel > 180 ? `text-md md:text-2xl` : |
|
zoomLevel > 140 ? `text-2xs md:text-2xl` : |
|
zoomLevel > 120 ? `text-3xs md:text-xl` : |
|
zoomLevel > 100 ? `text-4xs md:text-lg` : |
|
zoomLevel > 90 ? `text-5xs md:text-sm` : |
|
zoomLevel > 40 ? `md:text-xs` : `md:text-2xs`, |
|
|
|
showCaptions ? ( |
|
zoomLevel > 90 ? `block` : `hidden md:block` |
|
) : `hidden`, |
|
)} |
|
>{caption || ""} |
|
</div> |
|
{rendered.assetUrl && |
|
<img |
|
ref={ref} |
|
src={imageWithText || rendered.assetUrl} |
|
width={width} |
|
height={height} |
|
alt={rendered.alt} |
|
className={cn( |
|
`comic-panel w-full h-full object-cover max-w-max`, |
|
// showCaptions ? `-mt-11` : '' |
|
)} |
|
/>} |
|
</div> |
|
) |
|
} |