diff --git a/apps/landing/src/components/hero.tsx b/apps/landing/src/components/hero.tsx index 824cd50a..a1e7ae8f 100644 --- a/apps/landing/src/components/hero.tsx +++ b/apps/landing/src/components/hero.tsx @@ -1,5 +1,6 @@ import { Github } from "lucide-react"; import { FadeIn } from "./fade-in"; +import { TypingCursor } from "./typing-cursor"; export function Hero() { return ( @@ -21,9 +22,8 @@ export function Hero() { -

- 50+ image processing tools with local AI. Self-hosted, air-gapped, and fully offline. No - data ever leaves your network. +

+

diff --git a/apps/landing/src/components/typing-cursor.tsx b/apps/landing/src/components/typing-cursor.tsx new file mode 100644 index 00000000..688c325d --- /dev/null +++ b/apps/landing/src/components/typing-cursor.tsx @@ -0,0 +1,43 @@ +"use client"; + +import { AnimatePresence, motion } from "framer-motion"; +import { useCallback, useEffect, useState } from "react"; + +const phrases = [ + "100% local processing.", + "Zero data leaves your network.", + "50+ image tools.", + "14 AI models. Your hardware.", + "Air-gapped ready.", + "Enterprise-grade. Free forever.", + "One Docker container.", + "Open source. Always.", +]; + +export function TypingCursor() { + const [index, setIndex] = useState(0); + + const advance = useCallback(() => { + setIndex((i) => (i + 1) % phrases.length); + }, []); + + useEffect(() => { + const timer = setInterval(advance, 3000); + return () => clearInterval(timer); + }, [advance]); + + return ( + + + {phrases[index]} + + + ); +}