mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-06 08:16:03 +00:00
added framer animations
This commit is contained in:
parent
38d83dcbce
commit
21d0e6391e
8 changed files with 107 additions and 96 deletions
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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={{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue