diff --git a/components/FamousTemplate.tsx b/components/FamousTemplate.tsx index 7abd552..320fc6a 100644 --- a/components/FamousTemplate.tsx +++ b/components/FamousTemplate.tsx @@ -46,7 +46,7 @@ const FamousTemplate: React.FC = ({ showTabs = true }) => { return (
-
+

Inspiration Lab

@@ -58,7 +58,7 @@ const FamousTemplate: React.FC = ({ showTabs = true }) => { type="button" data-alt={`template-tab-${tab}`} onClick={() => setActiveTab(tab)} - className={`px-3 py-1 rounded-none text-sm transition-colors border ${ + className={`px-3 py-0.5 rounded-none text-sm transition-colors border ${ activeTab === tab ? "border-white/60 bg-white/80 text-slate-900" : "border-white/20 text-white/80 hover:border-white/40 hover:bg-white/10" @@ -83,7 +83,7 @@ const FamousTemplate: React.FC = ({ showTabs = true }) => {
{ const v = e.currentTarget.querySelector('video') as HTMLVideoElement | null v?.play?.() @@ -150,7 +150,7 @@ const FamousTemplate: React.FC = ({ showTabs = true }) => { + + + {/* Configuration - Desktop: Full Panel, Mobile: Setting Icon */} diff --git a/components/pages/create-video/CreateVideo.tsx b/components/pages/create-video/CreateVideo.tsx index 4346693..e6ee688 100644 --- a/components/pages/create-video/CreateVideo.tsx +++ b/components/pages/create-video/CreateVideo.tsx @@ -5,10 +5,10 @@ import FamousTemplate from '@/components/FamousTemplate'; export default function CreateVideo() { return ( -
+
Your idea. A movie. In minutes.
-
Our AI turns sparks into full-blown stories — fast & free.
-
+
Our AI turns sparks into full-blown stories — fast & free.
+
diff --git a/hooks/useTypewriterText.ts b/hooks/useTypewriterText.ts new file mode 100644 index 0000000..39c76c3 --- /dev/null +++ b/hooks/useTypewriterText.ts @@ -0,0 +1,62 @@ +import { useEffect, useMemo, useState } from 'react'; + +/** + * Renders a typewriter-style text that cycles through a list of strings. + * It types each string character-by-character, pauses, clears, then proceeds to the next. + * @param {string[]} strings - The list of sentences to cycle through. + * @param {{ typingMs?: number; pauseMs?: number; resetMs?: number }} [options] - Timing options. + * @returns {string} - The current text to display. + */ +export function useTypewriterText( + strings: string[], + options?: { typingMs?: number; pauseMs?: number; resetMs?: number } +): string { + const typingMs = options?.typingMs ?? 40; + const pauseMs = options?.pauseMs ?? 1200; + const resetMs = options?.resetMs ?? 300; + + const [text, setText] = useState(''); + const [listIndex, setListIndex] = useState(0); + const [charIndex, setCharIndex] = useState(0); + const [phase, setPhase] = useState<'typing' | 'pausing' | 'clearing'>('typing'); + const key = useMemo(() => (strings && strings.length ? strings.join('|') : ''), [strings]); + + useEffect(() => { + setText(''); + setListIndex(0); + setCharIndex(0); + setPhase('typing'); + }, [key]); + + useEffect(() => { + const list = strings && strings.length ? strings : ['Describe the story you want to make...']; + const full = list[listIndex % list.length] ?? ''; + let timer: number | undefined; + + if (phase === 'typing') { + if (charIndex <= full.length) { + setText(full.slice(0, charIndex)); + timer = window.setTimeout(() => setCharIndex(charIndex + 1), typingMs); + } else { + setPhase('pausing'); + } + } else if (phase === 'pausing') { + timer = window.setTimeout(() => setPhase('clearing'), pauseMs); + } else { + setText(''); + timer = window.setTimeout(() => { + setListIndex((listIndex + 1) % list.length); + setCharIndex(0); + setPhase('typing'); + }, resetMs); + } + + return () => { + if (timer) window.clearTimeout(timer); + }; + }, [strings, listIndex, charIndex, phase, typingMs, pauseMs, resetMs]); + + return text; +} + +