jbilcke-hf's picture
jbilcke-hf HF staff
add story extension
5443f48
raw
history blame
13.4 kB
"use client"
import React from "react"
import { IoMdPhonePortrait } from "react-icons/io"
import { GiRollingDices } from "react-icons/gi"
import { ClapMediaOrientation } from "@aitube/clap"
import { Card, CardContent, CardHeader } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Toaster } from "@/components/ui/sonner"
import { TextareaField } from "@/components/form/textarea-field"
import { cn, generateRandomStory } from "@/lib/utils"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
import { useIsBusy, useOrientation, useQueryStringParams, useStoryPromptDraft } from "@/lib/hooks"
import { BottomBar, VideoPreview } from "@/components/interface"
import { MainTitle } from "@/components/interface/main-title"
import { LoadClapButton } from "@/components/interface/load-clap-button"
import { SaveClapButton } from "@/components/interface/save-clap-button"
import { useProcessors } from "@/lib/hooks/useProcessors"
import { Characters } from "@/components/interface/characters"
import { useOAuth } from "@/lib/oauth/useOAuth"
import { AuthWall } from "@/components/interface/auth-wall"
import { defaultPrompt } from "./config"
import { useStore } from "./store"
export function Main() {
const { storyPromptDraft, setStoryPromptDraft, promptDraftRef } = useStoryPromptDraft()
const { isBusy } = useIsBusy()
const { orientation, toggleOrientation } = useOrientation()
const { handleCreateStory, handleExtendStory } = useProcessors()
useQueryStringParams()
const currentVideo = useStore(s => s.currentVideo)
const showAuthWall = useStore(s => s.showAuthWall)
const { isLoggedIn, enableOAuthWall } = useOAuth()
return (
<TooltipProvider>
<div className={cn(
`fixed`,
// `bg-zinc-800`,
// old style, more "amber"
// `bg-gradient-to-br from-amber-600 to-yellow-500`,
// nice style!
// `bg-gradient-to-br from-amber-700 to-yellow-300`,
// warm orange, a bit flash but not bad, not bad at all
// `bg-gradient-to-br from-orange-700 to-yellow-400`,
// nice "AiTube" vibe
// `bg-gradient-to-br from-red-700 to-yellow-400`,
// pretty cool lime!
`bg-gradient-to-br from-lime-700 to-yellow-400`,
// new style, pretty "fresh" - maybe too bright?
// use a dark logo for this one
// `bg-gradient-to-br from-yellow-200 to-yellow-500`,
// too pastel
// `bg-gradient-to-br from-yellow-200 to-red-300`,
// `bg-gradient-to-br from-sky-400 to-sky-300/30`,
`w-screen h-full overflow-y-scroll md:overflow-hidden`,
)}
// this version is a bit too aggressive on mobile
// style={{ boxShadow: "inset 0 0px 250px 0 rgb(0 0 0 / 60%)" }}
style={{ boxShadow: "inset 0 0 10vh 0 rgb(0 0 0 / 50%)" }}
>
<div className="flex flex-col w-screen h-screen">
<div className="
flex flex-col md:flex-row w-full
items-center md:justify-center
flex-1
"
>
<div className={cn(
`flex flex-col md:h-full md:items-center md:justify-center`,
`w-full md:w-1/2`,
`transition-all duration-200 ease-in-out`,
`ml-0`,
`pt-4 sm:pt-0`,
)}>
<Card className={cn(
// `shadow-2xl z-30 rounded-3xl`,
`shadow-none`,
`w-full md:ml-[12%] md:w-[95%]`,
`transition-all duration-200 ease-in-out`,
`bg-transparent dark:bg-transparent`,
// `backdrop-blur-2xl dark:backdrop-blur-2xl`,
// `bg-amber-500 dark:bg-amber-500`,
`border-transparent dark:border-transparent`,
// `bg-stone-50/90 dark:bg-stone-50/90`,
// `border-yellow-100 dark:border-yellow-100`,
// `bg-yellow-500 dark:bg-yellow-500`,
// `border-yellow-400 dark:border-yellow-400`,
)}>
<CardHeader>
<MainTitle />
</CardHeader>
<CardContent
className="flex flex-col space-y-3"
>
{/* LEFT MENU BUTTONS + MAIN PROMPT INPUT */}
<div className="flex flex-row space-x-3 w-full">
{/*
TODO: To finish by Julian a bit later
<div className="
flex flex-col
w-32 bg-yellow-600
transition-all duration-200 ease-in-out
space-y-2 md:space-y-4
">
<Input
type="file"
className=""
onChange={async (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files.length > 0) {
const file = e.target.files[0];
const newImageBase64 = await fileToBase64(file)
setMainCharacterImage(newImageBase64)
}
}}
accept="image/*"
/>
</div>
*/}
{/* MAIN PROMPT INPUT */}
<div className="
flex flex-col
flex-1
transition-all duration-200 ease-in-out
space-y-2 md:space-y-4
">
<TextareaField
id="story-prompt-draft"
// label="My story:"
// disabled={modelState != 'ready'}
onChange={(e) => {
setStoryPromptDraft(e.target.value)
promptDraftRef.current = e.target.value
}}
placeholder={defaultPrompt}
inputClassName="
transition-all duration-200 ease-in-out
h-32 md:h-56 lg:h-64
"
disabled={isBusy}
value={storyPromptDraft}
/>
{/* END OF MAIN PROMPT INPUT */}
</div>
{/* END OF LEFT MENU BUTTONS + MAIN PROMPT INPUT */}
</div>
{/* ACTION BAR */}
<div className="
w-full
flex flex-col lg:flex-row
justify-between items-center
space-y-3 lg:space-x-3 lg:space-y-0">
{/*
<Button
onClick={() => load()}
disabled={isBusy}
// variant="ghost"
className={cn(
`text-sm md:text-base lg:text-lg`,
`bg-stone-800/90 text-amber-400/100 dark:bg-stone-800/90 dark:text-amber-400/100`,
`font-bold`,
`hover:bg-stone-800/100 hover:text-amber-300/100 dark:hover:bg-stone-800/100 dark:hover:text-amber-300/100`,
storyPromptDraft ? "opacity-100" : "opacity-80"
)}
>
<span className="mr-1">Load project</span>
</Button>
*/}
<div className="
flex flex-row
justify-between items-center
space-x-3">
<LoadClapButton />
<SaveClapButton />
<Characters />
</div>
<div className="
flex flex-row
justify-between items-center
space-x-3
select-none
">
{/* RANDOMNESS SWITCH */}
<Tooltip>
<TooltipTrigger asChild><div className="
flex flex-row
justify-between items-center
cursor-pointer
transition-all duration-150 ease-in-out
hover:scale-110 active:scale-150
text-stone-800
hover:text-stone-950
active:text-black
group
"
onClick={() => {
const randomStory = generateRandomStory()
setStoryPromptDraft(randomStory)
promptDraftRef.current = randomStory
}}>
<div>
</div>
<div className="
w-6 h-8
flex flex-row items-center justify-center
transition-all duration-150 ease-out
group-hover:animate-swing
"
>
<GiRollingDices size={24} />
</div>
</div></TooltipTrigger>
<TooltipContent side="top">
<p className="text-xs font-normal text-stone-100/90 text-center">
Generate a random prompt.
</p>
</TooltipContent>
</Tooltip>
{/* END OF RANDOMNESS SWITCH */}
{/* ORIENTATION SWITCH */}
<div className="
flex flex-row
justify-between items-center
cursor-pointer
transition-all duration-150 ease-out
hover:scale-110 active:scale-150
text-stone-800
hover:text-stone-950
active:text-black
group
"
onClick={() => toggleOrientation()}>
<div>
</div>
<div className="
w-8 h-8
flex flex-row items-center justify-center
transition-all duration-150 ease-in-out
group-hover:animate-swing
"
>
<div className={cn(
`transition-all duration-200 ease-in-out`,
orientation === ClapMediaOrientation.LANDSCAPE ? `rotate-90` : `rotate-0`
)}>
<IoMdPhonePortrait size={24} />
</div>
</div>
</div>
{/* END OF ORIENTATION SWITCH */}
<Button
onClick={currentVideo ? handleExtendStory : handleCreateStory}
disabled={!storyPromptDraft || isBusy || !isLoggedIn}
// variant="ghost"
className={cn(
`text-base md:text-lg lg:text-xl xl:text-2xl`,
`bg-stone-800/90 text-amber-400/100 dark:bg-stone-800/90 dark:text-amber-400/100`,
`font-bold`,
`hover:bg-stone-800/100 hover:text-amber-300/100 dark:hover:bg-stone-800/100 dark:hover:text-amber-300/100`,
storyPromptDraft ? "opacity-100" : "opacity-80"
)}
>
<span className="mr-1.5">{
currentVideo ? 'Extend' : 'Create'
}</span><span className="hidden md:inline">πŸ‘‰</span><span className="inline md:hidden">πŸ‘‡</span>
</Button>
</div>
{/* END OF ACTION BAR */}
</div>
</CardContent>
</Card>
</div>
<div className={cn(
`flex flex-col items-center justify-center`,
`flex-1 h-full`,
// `transition-all duration-200 ease-in-out`
`-mt-[20px] -mb-[90px] md:-mt-0 md:-mb-0`,
)}>
<VideoPreview />
</div>
</div>
<BottomBar />
</div>
<Toaster />
<AuthWall show={showAuthWall} />
</div>
</TooltipProvider>
);
}