diff options
| author | Dhravya Shah <[email protected]> | 2025-09-18 20:15:59 -0700 |
|---|---|---|
| committer | Dhravya Shah <[email protected]> | 2025-09-18 20:15:59 -0700 |
| commit | 8e784b3889b90d1a61929ef6672bb7272065f542 (patch) | |
| tree | 2bab5938bcd4fff75bf7300d90b8cc2cc3978f95 | |
| parent | fix: one more build error (diff) | |
| download | supermemory-09-18-newish_get_started_page.tar.xz supermemory-09-18-newish_get_started_page.zip | |
newish get started page09-18-newish_get_started_page
| -rw-r--r-- | apps/web/components/get-started.tsx | 362 | ||||
| -rw-r--r-- | apps/web/components/views/add-memory/fixed-mutation.tsx | 244 | ||||
| -rw-r--r-- | apps/web/public/3d-shapes/1.png | bin | 0 -> 190401 bytes | |||
| -rw-r--r-- | apps/web/public/3d-shapes/2.png | bin | 0 -> 345350 bytes | |||
| -rw-r--r-- | apps/web/public/3d-shapes/3.png | bin | 0 -> 335218 bytes | |||
| -rw-r--r-- | apps/web/public/3d-shapes/4.png | bin | 0 -> 285206 bytes | |||
| -rw-r--r-- | apps/web/public/3d-shapes/5.png | bin | 0 -> 149253 bytes | |||
| -rw-r--r-- | apps/web/public/images/onboarding.png | bin | 0 -> 5392831 bytes |
8 files changed, 606 insertions, 0 deletions
diff --git a/apps/web/components/get-started.tsx b/apps/web/components/get-started.tsx new file mode 100644 index 00000000..1619ca2e --- /dev/null +++ b/apps/web/components/get-started.tsx @@ -0,0 +1,362 @@ +import { AnimatePresence, motion } from "framer-motion" +import { Brain, Chrome, ExternalLink, Plus } from "lucide-react" +import { useEffect, useState } from "react" +import { ConnectAIModal } from "@/components/connect-ai-modal" +import { analytics } from "@/lib/analytics" + +interface GetStartedProps { + setShowAddMemoryView?: (show: boolean) => void +} + +export const GetStarted = ({ setShowAddMemoryView }: GetStartedProps) => { + const [imageLoaded, setImageLoaded] = useState(false) + const [showContent, setShowContent] = useState(false) + const [showConnectAIModal, setShowConnectAIModal] = useState(false) + + useEffect(() => { + const img = new Image() + img.onload = () => { + setImageLoaded(true) + setTimeout(() => setShowContent(true), 100) + } + img.src = "/images/onboarding.png" + }, []) + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + duration: 0.8, + ease: [0.25, 0.1, 0.25, 1] as const, + staggerChildren: 0.15, + delayChildren: 0.2, + }, + }, + } + + const leftColumnVariants = { + hidden: { + opacity: 0, + x: -40, + }, + visible: { + opacity: 1, + x: 0, + transition: { + duration: 1, + ease: [0.25, 0.1, 0.25, 1] as const, + staggerChildren: 0.1, + }, + }, + } + + const rightColumnVariants = { + hidden: { + opacity: 0, + x: 40, + }, + visible: { + opacity: 1, + x: 0, + transition: { + duration: 0.8, + ease: [0.25, 0.1, 0.25, 1] as const, + delay: 0.6, + staggerChildren: 0.1, + }, + }, + } + + const textLineVariants = { + hidden: { + opacity: 0, + y: 30, + }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.8, + ease: [0.25, 0.1, 0.25, 1] as const, + }, + }, + } + + const cardVariants = { + hidden: { + opacity: 0, + y: 20, + scale: 0.95, + }, + visible: { + opacity: 1, + y: 0, + scale: 1, + transition: { + duration: 0.6, + ease: [0.25, 0.1, 0.25, 1] as const, + }, + }, + } + + const backgroundVariants = { + hidden: { + scale: 1.1, + opacity: 0, + }, + visible: { + scale: 1, + opacity: 1, + transition: { + duration: 1.5, + ease: [0.25, 0.1, 0.25, 1] as const, + }, + }, + } + + const handleChromeExtension = () => { + analytics.extensionInstallClicked() + window.open( + "https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc", + "_blank", + ) + } + + const handleAddMemory = () => { + setShowConnectAIModal(false) + if (setShowAddMemoryView) { + setShowAddMemoryView(true) + } + } + + return ( + <div className="fixed inset-0 overflow-hidden bg-black"> + <AnimatePresence> + {!imageLoaded && ( + <motion.div + className="absolute inset-0 bg-gradient-to-br from-zinc-950 to-zinc-900 flex items-center justify-center z-[1000]" + exit={{ opacity: 0 }} + initial={{ opacity: 1 }} + transition={{ duration: 0.5 }} + > + <motion.div + animate={{ + opacity: [0.3, 1, 0.3], + scale: [1, 1.05, 1], + }} + className="text-white/80 text-xl font-medium tracking-widest" + transition={{ + duration: 2, + repeat: Number.POSITIVE_INFINITY, + ease: "easeInOut", + }} + > + SUPERMEMORY + </motion.div> + </motion.div> + )} + </AnimatePresence> + + {imageLoaded && ( + <> + <motion.div + animate="visible" + className="absolute inset-0 bg-cover bg-center bg-no-repeat" + initial="hidden" + style={{ + backgroundImage: "url('/images/onboarding.png')", + }} + variants={backgroundVariants} + /> + + <motion.div + animate={{ opacity: 1 }} + className="absolute inset-0" + initial={{ opacity: 0 }} + style={{ + background: ` + radial-gradient(circle at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 50%), + linear-gradient(135deg, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.4) 50%, rgba(0,0,0,0.6) 100%) + `, + }} + transition={{ duration: 1, delay: 0.5 }} + /> + + <AnimatePresence> + {showContent && ( + <motion.div + animate="visible" + className="relative h-screen grid grid-cols-1 lg:grid-cols-2 items-center p-6 md:p-16 gap-8 lg:gap-16 max-w-7xl mx-auto" + initial="hidden" + variants={containerVariants} + > + {/* Left Column - Quote */} + <motion.div + className="flex flex-col justify-center order-2 lg:order-1" + variants={leftColumnVariants} + > + <motion.div + className="text-2xl md:text-4xl lg:text-5xl xl:text-6xl font-black leading-none text-white mb-2 md:mb-4 tracking-tight font-space-grotesk" + style={{ + textShadow: "0 8px 32px rgba(0,0,0,0.6)", + }} + variants={textLineVariants} + > + <span + className="bg-gradient-to-r from-white to-gray-200 bg-clip-text text-transparent" + style={{ + filter: "drop-shadow(0 0 20px rgba(255,255,255,0.1))", + }} + > + Intelligence without memory + </span> + </motion.div> + + <motion.div + className="text-2xl md:text-4xl lg:text-5xl xl:text-6xl font-black leading-none text-white mb-2 md:mb-4 tracking-tight font-space-grotesk" + style={{ + textShadow: "0 8px 32px rgba(0,0,0,0.6)", + }} + variants={textLineVariants} + > + <span + className="bg-gradient-to-r from-white to-gray-200 bg-clip-text text-transparent" + style={{ + filter: "drop-shadow(0 0 20px rgba(255,255,255,0.1))", + }} + > + is just sophisticated + </span> + </motion.div> + + <motion.div + className="text-2xl md:text-4xl lg:text-5xl xl:text-6xl font-black leading-none tracking-tight font-space-grotesk" + style={{ + textShadow: "0 8px 32px rgba(0,0,0,0.6)", + }} + variants={textLineVariants} + > + <span + className="bg-gradient-to-r from-blue-500 via-cyan-500 to-blue-600 bg-clip-text text-transparent" + style={{ + filter: "drop-shadow(0 0 20px rgba(59, 130, 246, 0.3))", + }} + > + randomness + </span> + </motion.div> + </motion.div> + + {/* Right Column - Actions */} + <motion.div + className="flex md:flex-col justify-center order-1 lg:order-2 flex-col-reverse" + variants={rightColumnVariants} + > + {/* Mobile Chrome Extension Link */} + <motion.p + className="text-sm text-blue-400 hover:text-blue-300 transition-colors underline cursor-pointer mb-4 lg:hidden text-center mt-4 md:mt-0" + onClick={handleChromeExtension} + variants={textLineVariants} + > + Download Chrome extension to use with ChatGPT + </motion.p> + + <div className="flex flex-col gap-5"> + {/* Chrome Extension Card - Hidden on mobile */} + <motion.button + className="hidden lg:flex bg-white/8 backdrop-blur-xl border border-white/10 rounded-xl p-6 text-left cursor-pointer transition-all duration-300 items-center gap-4 hover:bg-white/12 hover:border-white/20" + onClick={handleChromeExtension} + variants={cardVariants} + whileHover={{ + scale: 1.02, + y: -2, + transition: { duration: 0.2, ease: "easeOut" }, + }} + whileTap={{ scale: 0.98 }} + > + <Chrome + className="text-amber-500 flex-shrink-0" + size={24} + /> + <div className="flex-1"> + <h3 className="text-lg font-semibold text-white mb-1"> + Add to Chrome + </h3> + <p className="text-sm text-white/70 leading-relaxed"> + Save items and use supermemory with ChatGPT and other + apps. Import your ChatGPT memories, twitter bookmarks, + and other data. + </p> + </div> + <ExternalLink + className="text-white/50 flex-shrink-0" + size={16} + /> + </motion.button> + + {/* Connect AI Card */} + <ConnectAIModal + onOpenChange={setShowConnectAIModal} + open={showConnectAIModal} + > + <motion.div + className="bg-white/8 backdrop-blur-xl border border-white/10 rounded-xl p-6 text-left cursor-pointer transition-all duration-300 flex items-center gap-4 hover:bg-white/12 hover:border-white/20" + variants={cardVariants} + whileHover={{ + scale: 1.02, + y: -2, + transition: { duration: 0.2, ease: "easeOut" }, + }} + whileTap={{ scale: 0.98 }} + > + <Brain + className="text-blue-500 flex-shrink-0" + size={24} + /> + <div className="flex-1"> + <h3 className="text-lg font-semibold text-white mb-1"> + Connect to AI + </h3> + <p className="text-sm text-white/70 leading-relaxed"> + Set up your AI connection for intelligent assistance + </p> + </div> + </motion.div> + </ConnectAIModal> + + {/* Add Memory Card */} + <motion.button + className="bg-white/8 backdrop-blur-xl border border-white/10 rounded-xl p-6 text-left cursor-pointer transition-all duration-300 flex items-center gap-4 hover:bg-white/12 hover:border-white/20" + onClick={handleAddMemory} + variants={cardVariants} + whileHover={{ + scale: 1.02, + y: -2, + transition: { duration: 0.2, ease: "easeOut" }, + }} + whileTap={{ scale: 0.98 }} + > + <Plus + className="text-emerald-500 flex-shrink-0" + size={24} + /> + <div className="flex-1"> + <h3 className="text-lg font-semibold text-white mb-1"> + Add Memory + </h3> + <p className="text-sm text-white/70 leading-relaxed"> + Start building your knowledge base + </p> + </div> + </motion.button> + </div> + </motion.div> + </motion.div> + )} + </AnimatePresence> + </> + )} + </div> + ) +} diff --git a/apps/web/components/views/add-memory/fixed-mutation.tsx b/apps/web/components/views/add-memory/fixed-mutation.tsx new file mode 100644 index 00000000..c71793b5 --- /dev/null +++ b/apps/web/components/views/add-memory/fixed-mutation.tsx @@ -0,0 +1,244 @@ +import { $fetch } from "@lib/api" +import { useMutation } from "@tanstack/react-query" +import { toast } from "sonner" + +// Simplified mutation that doesn't block UI with polling +export const createSimpleAddContentMutation = ( + queryClient: any, + onClose?: () => void, +) => { + return useMutation({ + mutationFn: async ({ + content, + project, + contentType, + }: { + content: string + project: string + contentType: "note" | "link" + }) => { + // Just create the memory, don't wait for processing + const response = await $fetch("@post/documents", { + body: { + content: content, + containerTags: [project], + metadata: { + sm_source: "consumer", + }, + }, + }) + + if (response.error) { + throw new Error( + response.error?.message || `Failed to add ${contentType}`, + ) + } + + return { id: response.data.id, contentType } + }, + onMutate: async ({ content, project, contentType }) => { + // Cancel any outgoing refetches + await queryClient.cancelQueries({ + queryKey: ["documents-with-memories", project], + }) + + // Snapshot the previous value + const previousMemories = queryClient.getQueryData([ + "documents-with-memories", + project, + ]) + + // Create optimistic memory + const tempId = `temp-${Date.now()}` + const optimisticMemory = { + id: tempId, + content: contentType === "link" ? "" : content, + url: contentType === "link" ? content : null, + title: + contentType === "link" ? "Processing..." : content.substring(0, 100), + description: + contentType === "link" + ? "Extracting content..." + : "Processing content...", + containerTags: [project], + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + status: "processing", + type: contentType, + metadata: { + processingStage: "queued", + processingMessage: "Added to processing queue", + }, + memoryEntries: [], + isOptimistic: true, + } + + // Optimistically update to include the new memory + queryClient.setQueryData( + ["documents-with-memories", project], + (old: any) => { + // Handle infinite query structure + if (old?.pages) { + const newPages = [...old.pages] + if (newPages.length > 0) { + // Add to the first page + const firstPage = { ...newPages[0] } + firstPage.documents = [ + optimisticMemory, + ...(firstPage.documents || []), + ] + newPages[0] = firstPage + } else { + // No pages yet, create the first page + newPages.push({ + documents: [optimisticMemory], + pagination: { currentPage: 1, totalPages: 1, totalCount: 1 }, + totalCount: 1, + }) + } + + return { + ...old, + pages: newPages, + } + } + // Fallback for regular query structure + const newData = old + ? { + ...old, + documents: [optimisticMemory, ...(old.documents || [])], + totalCount: (old.totalCount || 0) + 1, + } + : { documents: [optimisticMemory], totalCount: 1 } + return newData + }, + ) + + return { previousMemories, optimisticId: tempId } + }, + onError: (error, variables, context) => { + // Roll back on error + if (context?.previousMemories) { + queryClient.setQueryData( + ["documents-with-memories", variables.project], + context.previousMemories, + ) + } + toast.error(`Failed to add ${variables.contentType}`, { + description: error instanceof Error ? error.message : "Unknown error", + }) + }, + onSuccess: (data, variables, context) => { + // Show success message + toast.success( + `${variables.contentType === "link" ? "Link" : "Note"} created successfully!`, + ) + + // Close modal + onClose?.() + + // Start polling for this specific memory ID + // The polling will happen in the background and update the optimistic memory when done + startMemoryPolling( + data.id, + variables.project, + context?.optimisticId, + queryClient, + ) + }, + }) +} + +// Background polling function +const startMemoryPolling = ( + memoryId: string, + project: string, + optimisticId: string | undefined, + queryClient: any, +) => { + const pollMemory = async () => { + try { + const memory = await $fetch(`@get/documents/${memoryId}`) + + if (memory.error) { + console.error("Failed to fetch memory status:", memory.error) + return false + } + + const isComplete = + memory.data?.status === "done" || + memory.data?.content || + memory.data?.memoryEntries?.length > 0 + + if (isComplete) { + // Replace optimistic memory with real data + queryClient.setQueryData( + ["documents-with-memories", project], + (old: any) => { + if (old?.pages) { + // Handle infinite query structure + const newPages = old.pages.map((page: any) => ({ + ...page, + documents: page.documents.map((doc: any) => { + if (doc.isOptimistic || doc.id === optimisticId) { + // Replace with real memory + return { + ...memory.data, + isOptimistic: false, + } + } + return doc + }), + })) + + return { + ...old, + pages: newPages, + } + } + // Handle regular query structure + return { + ...old, + documents: old.documents.map((doc: any) => { + if (doc.isOptimistic || doc.id === optimisticId) { + return { + ...memory.data, + isOptimistic: false, + } + } + return doc + }), + } + }, + ) + return true // Stop polling + } + + return false // Continue polling + } catch (error) { + console.error("Error polling memory:", error) + return false // Continue polling + } + } + + // Poll every 3 seconds, max 60 attempts (3 minutes) + let attempts = 0 + const maxAttempts = 60 + + const poll = async () => { + if (attempts >= maxAttempts) { + console.log("Memory polling timed out") + return + } + + const isComplete = await pollMemory() + attempts++ + + if (!isComplete && attempts < maxAttempts) { + setTimeout(poll, 3000) // Poll again in 3 seconds + } + } + + // Start polling after a short delay + setTimeout(poll, 2000) +} diff --git a/apps/web/public/3d-shapes/1.png b/apps/web/public/3d-shapes/1.png Binary files differnew file mode 100644 index 00000000..ebf3cf58 --- /dev/null +++ b/apps/web/public/3d-shapes/1.png diff --git a/apps/web/public/3d-shapes/2.png b/apps/web/public/3d-shapes/2.png Binary files differnew file mode 100644 index 00000000..aed06509 --- /dev/null +++ b/apps/web/public/3d-shapes/2.png diff --git a/apps/web/public/3d-shapes/3.png b/apps/web/public/3d-shapes/3.png Binary files differnew file mode 100644 index 00000000..c1d6775e --- /dev/null +++ b/apps/web/public/3d-shapes/3.png diff --git a/apps/web/public/3d-shapes/4.png b/apps/web/public/3d-shapes/4.png Binary files differnew file mode 100644 index 00000000..a89c75fa --- /dev/null +++ b/apps/web/public/3d-shapes/4.png diff --git a/apps/web/public/3d-shapes/5.png b/apps/web/public/3d-shapes/5.png Binary files differnew file mode 100644 index 00000000..6eb298b1 --- /dev/null +++ b/apps/web/public/3d-shapes/5.png diff --git a/apps/web/public/images/onboarding.png b/apps/web/public/images/onboarding.png Binary files differnew file mode 100644 index 00000000..5be183fd --- /dev/null +++ b/apps/web/public/images/onboarding.png |