+
+ What I can do for you
+
+
+
+
+
+

+
+
+
Remember every context
+
+ I keep track of what you've saved and shared with your
+ supermemory.
+
+
+
+
+
+
+

+
+
+
Find when you need it
+
+ I surface the right memories inside
your supermemory,
+ superfast.
+
+
+
+
+
+
+

+
+
+
+ Grow with your supermemory
+
+
+ I learn and personalize over time, so every interaction feels
+ natural.
+
+
+
+
+
+
+ Add memories →
+
+
+
+ {/* Memories/Profile content */}
+
+ {onSubmit && (
+
+
+
+ )}
+
)
}
diff --git a/apps/web/components/new/onboarding/welcome/features-step.tsx b/apps/web/components/new/onboarding/welcome/features-step.tsx
deleted file mode 100644
index 2671efc1..00000000
--- a/apps/web/components/new/onboarding/welcome/features-step.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { motion } from "motion/react"
-import { Button } from "@ui/components/button"
-import { useRouter } from "next/navigation"
-import { cn } from "@lib/utils"
-import { dmSansClassName } from "@/lib/fonts"
-
-export function FeaturesStep() {
- const router = useRouter()
-
- const handleContinue = () => {
- router.push("/new/onboarding?flow=welcome&step=memories")
- }
- return (
-
-
- What I can do for you
-
-
-
-
-
-

-
-
-
Remember every context
-
- I keep track of what you've saved and shared with your
- supermemory.
-
-
-
-
-
-
-

-
-
-
Find when you need it
-
- I surface the right memories inside
your supermemory,
- superfast.
-
-
-
-
-
-
-

-
-
-
Grow with your supermemory
-
- I learn and personalize over time, so every interaction feels
- natural.
-
-
-
-
-
-
-
- Add memories →
-
-
-
- )
-}
diff --git a/apps/web/components/new/onboarding/welcome/profile-step.tsx b/apps/web/components/new/onboarding/welcome/profile-step.tsx
index 65eb21c6..34393dad 100644
--- a/apps/web/components/new/onboarding/welcome/profile-step.tsx
+++ b/apps/web/components/new/onboarding/welcome/profile-step.tsx
@@ -292,7 +292,7 @@ export function ProfileStep({ onSubmit }: ProfileStepProps) {
otherLinks: otherLinks.filter((l) => l.trim()),
}
onSubmit(formData)
- router.push("/new/onboarding?flow=setup&step=relatable")
+ router.push("/new/onboarding/setup?step=relatable")
}}
>
{isSubmitting ? "Fetching..." : "Remember this →"}
diff --git a/apps/web/components/views/chat/chat-messages.tsx b/apps/web/components/views/chat/chat-messages.tsx
index 55940a60..3e55dc23 100644
--- a/apps/web/components/views/chat/chat-messages.tsx
+++ b/apps/web/components/views/chat/chat-messages.tsx
@@ -20,7 +20,7 @@ import { Streamdown } from "streamdown"
import { TextShimmer } from "@/components/text-shimmer"
import { usePersistentChat, useProject } from "@/stores"
import { useGraphHighlights } from "@/stores/highlights"
-import { modelNames, ModelIcon } from "@/lib/models"
+import { ModelIcon } from "@/lib/models"
import { Spinner } from "../../spinner"
import { areUIMessageArraysEqual } from "@/stores/chat"
@@ -270,7 +270,6 @@ export function ChatMessages() {
},
},
}),
- maxSteps: 10,
onFinish: (result) => {
const activeId = activeChatIdRef.current
if (!activeId) return
diff --git a/apps/web/hooks/use-document-mutations.ts b/apps/web/hooks/use-document-mutations.ts
index f3931b5f..bafa0e58 100644
--- a/apps/web/hooks/use-document-mutations.ts
+++ b/apps/web/hooks/use-document-mutations.ts
@@ -3,12 +3,28 @@
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { toast } from "sonner"
import { $fetch } from "@lib/api"
+import type { DocumentsWithMemoriesResponseSchema } from "@repo/validation/api"
+import type { z } from "zod"
+
+type DocumentsResponse = z.infer
+
+interface DocumentWithId {
+ id?: string
+ customId?: string | null
+}
interface DocumentsQueryData {
- documents: unknown[]
+ documents: DocumentWithId[]
totalCount: number
}
+type InfiniteQueryData = {
+ pages: DocumentsResponse[]
+ pageParams: number[]
+}
+
+type QueryData = DocumentsQueryData | InfiniteQueryData
+
interface UseDocumentMutationsOptions {
onClose?: () => void
}
@@ -51,7 +67,7 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions =
])
const optimisticMemory = {
- id: `temp-${Date.now()}`,
+ id: `temp-${crypto.randomUUID()}`,
content: content,
url: null,
title: content.substring(0, 100),
@@ -134,7 +150,7 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions =
])
const optimisticMemory = {
- id: `temp-${Date.now()}`,
+ id: `temp-${crypto.randomUUID()}`,
content: "",
url: url,
title: "Processing...",
@@ -251,7 +267,7 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions =
])
const optimisticMemory = {
- id: `temp-file-${Date.now()}`,
+ id: `temp-file-${crypto.randomUUID()}`,
content: "",
url: null,
title: title || file.name,
@@ -341,10 +357,100 @@ export function useDocumentMutations({ onClose }: UseDocumentMutationsOptions =
},
})
+ const deleteMutation = useMutation({
+ mutationFn: async ({ documentId }: { documentId: string }) => {
+ const response = await $fetch("@delete/documents/:id", {
+ params: { id: documentId },
+ })
+
+ if (response.error) {
+ throw new Error(response.error?.message || "Failed to delete document")
+ }
+
+ return response.data
+ },
+ onMutate: async ({ documentId }) => {
+ await queryClient.cancelQueries({
+ queryKey: ["documents-with-memories"],
+ })
+
+ const previousQueries = queryClient.getQueriesData({
+ queryKey: ["documents-with-memories"],
+ })
+
+ queryClient.setQueriesData(
+ { queryKey: ["documents-with-memories"] },
+ (old: QueryData | undefined) => {
+ if (!old) return old
+
+ if ("pages" in old) {
+ const infiniteData = old as InfiniteQueryData
+ return {
+ ...infiniteData,
+ pages: infiniteData.pages.map((page) => {
+ if (!page?.documents) return page
+ return {
+ ...page,
+ documents: page.documents.filter(
+ (doc) => doc.id !== documentId && doc.customId !== documentId,
+ ),
+ pagination: page.pagination
+ ? {
+ ...page.pagination,
+ totalItems: Math.max(
+ 0,
+ (page.pagination.totalItems ?? 0) - 1,
+ ),
+ }
+ : page.pagination,
+ }
+ }),
+ }
+ }
+
+ if ("documents" in old) {
+ const queryData = old as DocumentsQueryData
+ return {
+ ...queryData,
+ documents: queryData.documents.filter(
+ (doc: DocumentWithId) => {
+ return doc.id !== documentId && doc.customId !== documentId
+ },
+ ),
+ totalCount: Math.max(0, (queryData.totalCount ?? 0) - 1),
+ }
+ }
+
+ return old
+ },
+ )
+
+ return { previousQueries }
+ },
+ onError: (_error, _variables, context) => {
+ if (context?.previousQueries) {
+ context.previousQueries.forEach(([queryKey, data]) => {
+ queryClient.setQueryData(queryKey, data)
+ })
+ }
+ toast.error("Failed to delete document", {
+ description: _error instanceof Error ? _error.message : "Unknown error",
+ })
+ },
+ onSuccess: () => {
+ toast.success("Document deleted successfully!")
+ queryClient.invalidateQueries({
+ queryKey: ["documents-with-memories"],
+ })
+ onClose?.()
+ },
+ })
+
return {
noteMutation,
linkMutation,
fileMutation,
updateMutation,
+ deleteMutation,
}
}
diff --git a/apps/web/lib/variants.ts b/apps/web/lib/variants.ts
new file mode 100644
index 00000000..1bfebb7c
--- /dev/null
+++ b/apps/web/lib/variants.ts
@@ -0,0 +1,95 @@
+import type { Variants } from "motion/react"
+
+export const contentVariants: Variants = {
+ visible: {
+ opacity: 1,
+ y: 0,
+ transition: {
+ duration: 0.5,
+ ease: "easeInOut",
+ delay: 0.2,
+ },
+ },
+ hiddenUp: {
+ opacity: 0,
+ y: -40,
+ transition: {
+ duration: 0,
+ },
+ },
+ hiddenDown: {
+ opacity: 0,
+ y: 40,
+ transition: {
+ duration: 0,
+ },
+ },
+}
+
+export const continueVariants: Variants = {
+ visible: {
+ opacity: 1,
+ y: 0,
+ transition: {
+ duration: 0.5,
+ ease: "easeInOut",
+ },
+ },
+ hidden: {
+ opacity: 0,
+ y: -40,
+ transition: {
+ duration: 0,
+ },
+ },
+}
+
+export const gapVariants: Variants = {
+ default: {
+ gap: 16,
+ transition: {
+ duration: 0.6,
+ ease: "easeOut",
+ },
+ },
+ minimized: {
+ gap: 0,
+ transition: {
+ duration: 0.6,
+ ease: "easeOut",
+ },
+ },
+}
+
+export const orbVariants: Variants = {
+ default: {
+ scale: 1,
+ padding: 48,
+ paddingTop: 0,
+ y: 0,
+ transition: {
+ duration: 0.8,
+ ease: "easeOut",
+ },
+ },
+ features: {
+ scale: 0.7,
+ padding: 0,
+ paddingTop: 0,
+ y: 0,
+ transition: {
+ duration: 0.8,
+ ease: "easeOut",
+ },
+ },
+ memories: {
+ scale: 0.4,
+ padding: 0,
+ paddingTop: 0,
+ y: 0,
+ transition: {
+ duration: 0.8,
+ ease: "easeOut",
+ },
+ },
+}