mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: restore animated phrase rotation without blinking cursor bar
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Github } from "lucide-react";
|
import { Github } from "lucide-react";
|
||||||
import { FadeIn } from "./fade-in";
|
import { FadeIn } from "./fade-in";
|
||||||
|
import { TypingCursor } from "./typing-cursor";
|
||||||
|
|
||||||
export function Hero() {
|
export function Hero() {
|
||||||
return (
|
return (
|
||||||
@@ -21,9 +22,8 @@ export function Hero() {
|
|||||||
</FadeIn>
|
</FadeIn>
|
||||||
|
|
||||||
<FadeIn delay={0.1}>
|
<FadeIn delay={0.1}>
|
||||||
<p className="mx-auto mt-6 max-w-2xl text-lg text-muted md:text-xl">
|
<p className="mt-6 text-xl md:text-2xl">
|
||||||
50+ image processing tools with local AI. Self-hosted, air-gapped, and fully offline. No
|
<TypingCursor />
|
||||||
data ever leaves your network.
|
|
||||||
</p>
|
</p>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
|
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<AnimatePresence mode="wait">
|
||||||
|
<motion.span
|
||||||
|
key={phrases[index]}
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: -10 }}
|
||||||
|
transition={{ duration: 0.3 }}
|
||||||
|
className="text-accent"
|
||||||
|
>
|
||||||
|
{phrases[index]}
|
||||||
|
</motion.span>
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user