| "use client" |
|
|
| import { useEffect, useState, useTransition } from "react" |
|
|
| import { cn } from "@/lib/utils" |
| import { TopMenu } from "./interface/top-menu" |
| import { fonts } from "@/lib/fonts" |
| import { useStore } from "./store" |
| import { Zoom } from "./interface/zoom" |
| import { getStory } from "./queries/getStory" |
| import { BottomBar } from "./interface/bottom-bar" |
| import { Page } from "./interface/page" |
| import { LLMResponse } from "@/types" |
|
|
| export default function Main() { |
| const [_isPending, startTransition] = useTransition() |
|
|
| const isGeneratingStory = useStore(state => state.isGeneratingStory) |
| const setGeneratingStory = useStore(state => state.setGeneratingStory) |
|
|
| const font = useStore(state => state.font) |
| const preset = useStore(state => state.preset) |
| const prompt = useStore(state => state.prompt) |
|
|
| const setLayouts = useStore(state => state.setLayouts) |
|
|
| const setPanels = useStore(state => state.setPanels) |
| const setCaptions = useStore(state => state.setCaptions) |
|
|
| const zoomLevel = useStore(state => state.zoomLevel) |
|
|
| const [waitABitMore, setWaitABitMore] = useState(false) |
|
|
| |
| useEffect(() => { |
| if (!prompt) { return } |
|
|
| startTransition(async () => { |
| setWaitABitMore(false) |
| setGeneratingStory(true) |
|
|
| |
| const enableRateLimiter = false |
|
|
| const nbPanels = 4 |
|
|
| let llmResponse: LLMResponse = [] |
|
|
| try { |
| llmResponse = await getStory({ preset, prompt }) |
| console.log("LLM responded:", llmResponse) |
|
|
| } catch (err) { |
| console.log("LLM step failed due to:", err) |
| console.log("we are now switching to a degraded mode, using 4 similar panels") |
| |
| llmResponse = [] |
| for (let p = 0; p < nbPanels; p++) { |
| llmResponse.push({ |
| panel: p, |
| instructions: `${prompt} ${".".repeat(p)}`, |
| caption: "(Sorry, LLM generation failed: using degraded mode)" |
| }) |
| } |
| console.error(err) |
| } |
|
|
| |
|
|
| let limitedPrompt = prompt.slice(0, 77) |
| if (limitedPrompt.length !== prompt.length) { |
| console.log("Sorry folks, the prompt was cut to:", limitedPrompt) |
| } |
|
|
| const panelPromptPrefix = preset.imagePrompt(limitedPrompt).join(", ") |
|
|
| const newPanels: string[] = [] |
| const newCaptions: string[] = [] |
| setWaitABitMore(true) |
| console.log("Panel prompts for SDXL:") |
| for (let p = 0; p < nbPanels; p++) { |
| newCaptions.push(llmResponse[p]?.caption || "...") |
| const newPanel = [panelPromptPrefix, llmResponse[p]?.instructions || ""].map(chunk => chunk).join(", ") |
| newPanels.push(newPanel) |
| console.log(newPanel) |
| } |
| |
| setCaptions(newCaptions) |
| setPanels(newPanels) |
|
|
| setTimeout(() => { |
| setGeneratingStory(false) |
| setWaitABitMore(false) |
| }, enableRateLimiter ? 12000 : 0) |
| |
| }) |
| }, [prompt, preset?.label]) |
|
|
| return ( |
| <div> |
| <TopMenu /> |
| <div className={cn( |
| `flex items-start w-screen h-screen pt-24 md:pt-[72px] overflow-y-scroll`, |
| `transition-all duration-200 ease-in-out`, |
| zoomLevel > 105 ? `px-0` : `pl-1 pr-8 md:pl-16 md:pr-16`, |
| `print:pt-0 print:px-0 print:pl-0 print:pr-0`, |
| fonts.actionman.className |
| )}> |
| <div |
| className={cn( |
| `flex flex-col w-full`, |
| zoomLevel > 105 ? `items-start` : `items-center` |
| )}> |
| <div |
| className={cn( |
| `comic-page`, |
| `flex flex-col md:flex-row md:space-x-16 md:items-center md:justify-start`, |
| )} |
| style={{ |
| width: `${zoomLevel}%` |
| }}> |
| <Page page={0} /> |
| |
| {/* |
| // we could support multiple pages here, |
| // but let's disable it for now |
| <Page page={1} /> |
| */} |
| </div> |
| </div> |
| </div> |
| <Zoom /> |
| <BottomBar /> |
| <div className={cn( |
| `print:hidden`, |
| `z-20 fixed inset-0`, |
| `flex flex-row items-center justify-center`, |
| `transition-all duration-300 ease-in-out`, |
| isGeneratingStory |
| ? `bg-zinc-100/10 backdrop-blur-md` |
| : `bg-zinc-100/0 backdrop-blur-none pointer-events-none`, |
| fonts.actionman.className |
| )}> |
| <div className={cn( |
| `text-center text-xl text-stone-700 w-[70%]`, |
| isGeneratingStory ? ``: `scale-0 opacity-0`, |
| `transition-all duration-300 ease-in-out`, |
| )}> |
| {waitABitMore ? `Story is ready, but server is a bit busy!`: 'Generating a new story..'}<br/> |
| {waitABitMore ? `Please hold tight..` : ''} |
| </div> |
| </div> |
| </div> |
| ) |
| } |