added framer animations

This commit is contained in:
Mahesh Sanikommmu 2025-10-18 19:08:39 -07:00
parent 38d83dcbce
commit 21d0e6391e
8 changed files with 107 additions and 96 deletions

View file

@ -1,7 +1,7 @@
"use client"
import { useState } from "react"
import { motion } from "motion/react"
import { motion, AnimatePresence } from "motion/react"
import NovaOrb from "@/components/nova/nova-orb"
import { Button } from "@ui/components/button"
import { PanelRightCloseIcon, SendIcon } from "lucide-react"
@ -26,92 +26,90 @@ export function ChatSidebar() {
setIsChatOpen(!isChatOpen)
}
if (!isChatOpen) {
return (
<motion.div
className="absolute top-0 right-0 flex items-start justify-start m-4"
initial={{ x: "100%" }}
animate={{ x: 0 }}
transition={{ duration: 0.5, ease: "easeOut" }}
>
<motion.button
onClick={toggleChat}
className="flex items-center gap-2 rounded-full px-4 py-2 text-sm font-medium border-[1px] border-[#17181A]"
style={{
background: "linear-gradient(180deg, #0A0E14 0%, #05070A 100%)",
}}
whileTap={{ scale: 0.95 }}
return (
<AnimatePresence mode="wait">
{!isChatOpen ? (
<motion.div
key="closed"
className="absolute top-0 right-0 flex items-start justify-start m-4"
layoutId="chat-toggle-button"
>
Chat with Nova
</motion.button>
</motion.div>
)
}
return (
<motion.div
className="w-[450px] h-[calc(100vh-110px)] bg-[#0A0E14] backdrop-blur-md flex flex-col rounded-2xl m-4"
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%", opacity: 0, transition: { duration: 1, ease: "easeOut" } }}
transition={{ duration: 0.5, ease: "easeOut" }}
>
<motion.button
onClick={toggleChat}
className="absolute top-4 right-4 flex items-center gap-2 rounded-full p-2 text-xs"
style={{
background: "linear-gradient(180deg, #0A0E14 0%, #05070A 100%)",
}}
layoutId="chat-toggle-button"
>
<PanelRightCloseIcon className="size-4" />
Close chat
</motion.button>
<div className="flex-1 flex items-end justify-start px-4">
<div className="flex flex-col items-center gap-4">
<motion.div
className="flex items-center gap-2 text-foreground/70"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
<motion.button
onClick={toggleChat}
className="flex items-center gap-2 rounded-full px-4 py-2 text-xs font-medium border-[1px] border-[#17181A]"
style={{
background: "linear-gradient(180deg, #0A0E14 0%, #05070A 100%)",
}}
>
<NovaOrb size={24} />
<span className="text-sm">Waiting for your input</span>
</motion.div>
</div>
</div>
<div className="p-4">
<form
className="flex flex-col gap-3 bg-[#0D121A] rounded-xl p-2 relative"
onSubmit={(e) => {
e.preventDefault()
if (message.trim()) {
handleSend()
}
}}
Chat with Nova
</motion.button>
</motion.div>
) : (
<motion.div
key="open"
className="w-[450px] h-[calc(100vh-110px)] bg-[#0A0E14] backdrop-blur-md flex flex-col rounded-2xl m-4"
initial={{ x: "100px", opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: "100px", opacity: 0 }}
transition={{ duration: 0.3, ease: "easeOut", bounce: 0 }}
>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Chat with your Supermemory"
className="w-full text-foreground placeholder:text-foreground/20 rounded-sm outline-none resize-none text-base leading-relaxed bg-transparent px-2 h-10"
/>
<div className="flex justify-end absolute bottom-3 right-2">
<Button
type="submit"
disabled={!message.trim()}
className="text-foreground/20 hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed rounded-xl transition-all"
size="icon"
>
<SendIcon className="size-4" />
</Button>
<motion.button
onClick={toggleChat}
className="absolute top-4 right-4 flex items-center gap-2 rounded-full p-2 text-xs"
style={{
background: "linear-gradient(180deg, #0A0E14 0%, #05070A 100%)",
}}
layoutId="chat-toggle-button"
>
<PanelRightCloseIcon className="size-4" />
Close chat
</motion.button>
<div className="flex-1 flex items-end justify-start px-4">
<div className="flex flex-col items-center gap-4">
<motion.div
className="flex items-center gap-2 text-foreground/70"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
>
<NovaOrb size={24} />
<span className="text-sm">Waiting for your input</span>
</motion.div>
</div>
</div>
</form>
</div>
</motion.div>
<div className="p-4">
<form
className="flex flex-col gap-3 bg-[#0D121A] rounded-xl p-2 relative"
onSubmit={(e) => {
e.preventDefault()
if (message.trim()) {
handleSend()
}
}}
>
<input
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Chat with your Supermemory"
className="w-full text-foreground placeholder:text-foreground/20 rounded-sm outline-none resize-none text-base leading-relaxed bg-transparent px-2 h-10"
/>
<div className="flex justify-end absolute bottom-3 right-2">
<Button
type="submit"
disabled={!message.trim()}
className="text-foreground/20 hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed rounded-xl transition-all"
size="icon"
>
<SendIcon className="size-4" />
</Button>
</div>
</form>
</div>
</motion.div>
)}
</AnimatePresence>
)
}

View file

@ -7,7 +7,7 @@ import { IntegrationsStep } from "./integrations-step"
import { ChatSidebar } from "./chat-sidebar"
import { useState } from "react"
function AnimatedGradientBackground() {
export function AnimatedGradientBackground() {
return (
<div className="fixed inset-0 z-0 overflow-hidden">
<motion.div
@ -43,7 +43,7 @@ function AnimatedGradientBackground() {
}}
/>
<motion.div
className="absolute top-0 left-0 right-0 bottom-0 bg-[url('/bg-rectangle.png')] bg-cover bg-center bg-no-repeat"
className="absolute top-20 left-0 right-0 bottom-0 bg-[url('/bg-rectangle.png')] bg-cover bg-center bg-no-repeat"
initial={{ y: "100%" }}
animate={{ y: 0 }}
transition={{ duration: 0.75, ease: "easeOut", bounce: 0 }}

View file

@ -1,4 +1,4 @@
import { motion } from "framer-motion"
import { motion } from "motion/react"
import { Button } from "@ui/components/button"
import Link from "next/link"

View file

@ -1,4 +1,4 @@
import { motion } from "framer-motion"
import { motion } from "motion/react"
interface GreetingStepProps {
name: string
@ -7,7 +7,6 @@ interface GreetingStepProps {
export function GreetingStep({ name }: GreetingStepProps) {
return (
<motion.div
key="greeting"
className="text-center"
initial={{ opacity: 0, y: 0 }}
animate={{ opacity: 1, y: 0 }}

View file

@ -17,7 +17,6 @@ export function InputStep({
}: InputStepProps) {
return (
<motion.div
key="input"
className="text-center min-w-[250px]"
initial={{
opacity: 0,
@ -29,6 +28,12 @@ export function InputStep({
}}
exit={{
opacity: 0,
y: -10,
transition: {
duration: 0.5,
ease: "easeOut",
bounce: 0,
},
}}
transition={{
duration: 0.8,
@ -52,6 +57,7 @@ export function InputStep({
handleSubmit()
}
},
className: "!text-white placeholder:!text-white",
}}
onChange={(e) => setName((e.target as HTMLInputElement).value)}
style={{

View file

@ -12,6 +12,7 @@ import { FeaturesStep } from "./features-step"
import { Logo } from "@ui/assets/Logo"
import NovaOrb from "@/components/nova/nova-orb"
import { cn } from "@lib/utils"
import { AnimatedGradientBackground } from "../setup/page"
type OnboardStep = "input" | "greeting" | "welcome" | "username" | "features"
@ -80,6 +81,7 @@ export default function OnboardPage() {
showUserSupermemory={currentStep === "features"}
name={name}
/>
<AnimatedGradientBackground />
<div className="flex flex-col items-center justify-start h-[calc(100vh-86px)] relative">
<motion.div
className="absolute inset-0 bg-[url('/bg-rectangle.png')] bg-cover bg-center bg-no-repeat"
@ -90,7 +92,10 @@ export default function OnboardPage() {
}}
/>
<motion.div
className={cn("relative z-10 flex flex-col items-center justify-center", currentStep === "features" ? "mt-0" : "mt-16")}
className={cn(
"relative z-10 flex flex-col items-center justify-center",
currentStep === "features" ? "mt-0" : "mt-16",
)}
animate={{
gap: currentStep === "features" ? 0 : 16,
}}
@ -104,7 +109,7 @@ export default function OnboardPage() {
initial={{ opacity: 0, scale: 0.8 }}
animate={{
opacity: 1,
scale: currentStep === "features" ? 0.6 : 1,
scale: currentStep === "features" ? 0.7 : 1,
padding: currentStep === "features" ? 0 : 32,
}}
transition={{
@ -122,6 +127,7 @@ export default function OnboardPage() {
<AnimatePresence mode="wait">
{currentStep === "input" && (
<InputStep
key="input"
name={name}
setName={setName}
handleSubmit={handleSubmit}
@ -129,16 +135,19 @@ export default function OnboardPage() {
/>
)}
{currentStep === "greeting" && <GreetingStep name={name} />}
{currentStep === "greeting" && (
<GreetingStep key="greeting" name={name} />
)}
{currentStep === "welcome" && <WelcomeStep />}
{currentStep === "welcome" && <WelcomeStep key="welcome" />}
{currentStep === "username" && (
<ContinueStep
key="username"
setCurrentStep={(step) => setCurrentStep(step as OnboardStep)}
/>
)}
{currentStep === "features" && <FeaturesStep />}
{currentStep === "features" && <FeaturesStep key="features" />}
</AnimatePresence>
</motion.div>
</div>

View file

@ -3,7 +3,6 @@ import { motion } from "motion/react"
export function WelcomeStep() {
return (
<motion.div
key="welcome"
className="text-center"
initial={{ opacity: 0, y: 0 }}
animate={{ opacity: 1, y: 0 }}

View file

@ -16,7 +16,7 @@ export const Logo = ({
<title>supermemory Logo</title>
<path
d="M205.864 66.263h-76.401V0h-24.684v71.897c0 7.636 3.021 14.97 8.391 20.373l62.383 62.777 17.454-17.564-46.076-46.365h58.948v-24.84l-.015-.015ZM12.872 30.517l46.075 46.365H0v24.84h76.4v66.264h24.685V96.089c0-7.637-3.021-14.97-8.39-20.374l-62.37-62.762-17.453 17.564Z"
fill="currentColor"
fill="#ffffff"
/>
</svg>
);