|
"use client" |
|
|
|
import { useEffect, useTransition } from "react" |
|
import { useSearchParams } from "next/navigation" |
|
|
|
import { PresetName, defaultPreset, getPreset } from "@/app/engine/presets" |
|
|
|
import { cn } from "@/lib/utils" |
|
import { TopMenu } from "./interface/top-menu" |
|
import { FontName, defaultFont, fontList, fonts } from "@/lib/fonts" |
|
import { getRandomLayoutName, layouts } from "./layouts" |
|
import { useStore } from "./store" |
|
import { Zoom } from "./interface/zoom" |
|
import { getStory } from "./queries/getStory" |
|
|
|
export default function Main() { |
|
const [_isPending, startTransition] = useTransition() |
|
const searchParams = useSearchParams() |
|
|
|
const requestedPreset = (searchParams.get('preset') as PresetName) || defaultPreset |
|
const requestedFont = (searchParams.get('font') as FontName) || defaultFont |
|
const requestedPrompt = (searchParams.get('prompt') as string) || "" |
|
|
|
const isGeneratingStory = useStore(state => state.isGeneratingStory) |
|
const setGeneratingStory = useStore(state => state.setGeneratingStory) |
|
|
|
const font = useStore(state => state.font) |
|
const setFont = useStore(state => state.setFont) |
|
|
|
const preset = useStore(state => state.preset) |
|
const setPreset = useStore(state => state.setPreset) |
|
|
|
const prompt = useStore(state => state.prompt) |
|
const setPrompt = useStore(state => state.setPrompt) |
|
|
|
const layout = useStore(state => state.layout) |
|
const setLayout = useStore(state => state.setLayout) |
|
|
|
const setPanels = useStore(state => state.setPanels) |
|
|
|
const zoomLevel = useStore(state => state.zoomLevel) |
|
|
|
|
|
useEffect(() => { |
|
if (requestedPreset && requestedPreset !== preset.label) { setPreset(getPreset(requestedPreset)) } |
|
}, [requestedPreset]) |
|
|
|
useEffect(() => { |
|
if (requestedFont && requestedFont !== font) { setFont(requestedFont) } |
|
}, [requestedFont]) |
|
|
|
useEffect(() => { |
|
if (requestedPrompt && requestedPrompt !== prompt) { setPrompt(requestedPrompt) } |
|
}, [requestedPrompt]) |
|
|
|
|
|
useEffect(() => { |
|
if (!prompt) { return } |
|
|
|
startTransition(async () => { |
|
|
|
setGeneratingStory(true) |
|
|
|
const newLayout = getRandomLayoutName() |
|
console.log("using layout " + newLayout) |
|
setLayout(newLayout) |
|
|
|
try { |
|
const llmResponse = await getStory({ preset, prompt }) |
|
console.log("response:", llmResponse) |
|
|
|
|
|
const panelPromptPrefix = preset.imagePrompt(prompt).join(", ") |
|
console.log("panel prompt prefix:", panelPromptPrefix) |
|
|
|
const nbPanels = 4 |
|
const newPanels: string[] = [] |
|
|
|
for (let p = 0; p < nbPanels; p++) { |
|
const newPanel = [panelPromptPrefix, llmResponse[p] || ""] |
|
newPanels.push(newPanel.map(chunk => chunk).join(", ")) |
|
} |
|
console.log("newPanels:", newPanels) |
|
setPanels(newPanels) |
|
} catch (err) { |
|
console.error(err) |
|
} finally { |
|
setGeneratingStory(false) |
|
} |
|
}) |
|
}, [prompt, preset?.label]) |
|
|
|
const LayoutElement = (layouts as any)[layout] |
|
|
|
return ( |
|
<div> |
|
<TopMenu /> |
|
<div className={cn( |
|
`flex items-start w-screen h-screen pt-[120px] px-16 md:pt-[72px] overflow-y-scroll`, |
|
`transition-all duration-200 ease-in-out`, |
|
|
|
fonts.actionman.className |
|
)}> |
|
<div className="flex flex-col items-center w-full"> |
|
<div |
|
|
|
className={cn( |
|
`flex flex-col items-center justify-start`, |
|
|
|
// we are trying to reach a "book" look |
|
// we are using aspect-[297/210] because it matches A4 (297mm x 210mm) |
|
// `aspect-[210/297]`, |
|
`aspect-[250/297]`, |
|
|
|
`transition-all duration-100 ease-in-out`, |
|
`border border-stone-200`, |
|
`shadow-2xl` |
|
)} |
|
style={{ |
|
width: `${zoomLevel}%`, |
|
padding: `${Math.round((zoomLevel / 100) * 16)}px` |
|
}} |
|
> |
|
<LayoutElement /> |
|
</div> |
|
</div> |
|
</div> |
|
<Zoom /> |
|
<div className={cn( |
|
`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-lg text-stone-600 w-[70%]`, |
|
isGeneratingStory ? ``: `scale-0 opacity-0`, |
|
`transition-all duration-300 ease-in-out`, |
|
)}> |
|
Generating your story.. (hold tight) |
|
</div> |
|
</div> |
|
</div> |
|
) |
|
} |