+
+
+
+ {bio ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
Step {getStepNumberFor("bio")} of {totalSteps}
@@ -53,45 +92,6 @@ export function BioForm() {
value={bio}
onChange={(e) => setBio(e.target.value)}
/>
-
- {bio ? (
-
-
-
- ) : (
-
-
-
- )}
-
)
}
diff --git a/apps/web/app/onboarding/extension-form.tsx b/apps/web/app/onboarding/extension-form.tsx
index 721acb4fa..75f845573 100644
--- a/apps/web/app/onboarding/extension-form.tsx
+++ b/apps/web/app/onboarding/extension-form.tsx
@@ -816,7 +816,7 @@ export function ExtensionForm() {
const { totalSteps, nextStep, getStepNumberFor } = useOnboarding()
return (
-
+
@@ -827,21 +827,29 @@ export function ExtensionForm() {
Install the Chrome extension
- {/* Install the Supermemory extension to start saving and organizing everything that matters. */}
Bring Supermemory everywhere
-
+
+
Add to Chrome
@@ -891,17 +899,6 @@ export function ExtensionForm() {
-
-
-
)
}
diff --git a/apps/web/app/onboarding/mcp-form.tsx b/apps/web/app/onboarding/mcp-form.tsx
index d3152e428..161642c44 100644
--- a/apps/web/app/onboarding/mcp-form.tsx
+++ b/apps/web/app/onboarding/mcp-form.tsx
@@ -58,7 +58,46 @@ export function MCPForm() {
return (
-
+
+
+
+ {!isInstalling ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+
Step {getStepNumberFor("mcp")} of {totalSteps}
@@ -207,45 +246,6 @@ export function MCPForm() {
-
- {!isInstalling ? (
-
-
-
- ) : (
-
-
-
- )}
-
)
}
diff --git a/apps/web/components/graph-dialog.tsx b/apps/web/components/graph-dialog.tsx
index 9378b5925..2e74b3b12 100644
--- a/apps/web/components/graph-dialog.tsx
+++ b/apps/web/components/graph-dialog.tsx
@@ -62,6 +62,9 @@ export function GraphDialog() {
return response.data
},
getNextPageParam: (lastPage, allPages) => {
+ if (!lastPage || !lastPage.pagination) return undefined
+ if (!Array.isArray(allPages)) return undefined
+
const loaded = allPages.reduce(
(acc, p) => acc + (p.documents?.length ?? 0),
0,
diff --git a/apps/web/components/memories.tsx b/apps/web/components/memories.tsx
index 161d4f140..faf146c52 100644
--- a/apps/web/components/memories.tsx
+++ b/apps/web/components/memories.tsx
@@ -60,6 +60,9 @@ export function Memories() {
return response.data
},
getNextPageParam: (lastPage, allPages) => {
+ if (!lastPage || !lastPage.pagination) return undefined
+ if (!Array.isArray(allPages)) return undefined
+
const loaded = allPages.reduce(
(acc, p) => acc + (p.documents?.length ?? 0),
0,
@@ -177,42 +180,22 @@ export function Memories() {
}
return (
- <>
-
-
-
- {!isMobile ? (
-
-
-
-
-
-
-
-
-
- ) : (
+
+
+
+ {!isMobile ? (
+
@@ -221,6 +204,7 @@ export function Memories() {
onClick={(e) => {
e.stopPropagation()
setShowAddMemoryView(true)
+ setShowConnectAIModal(false)
}}
type="button"
>
@@ -229,17 +213,34 @@ export function Memories() {
- )}
-
-
-
- {showAddMemoryView && (
-
setShowAddMemoryView(false)}
- />
- )}
-
- >
+
+ ) : (
+
+
+
+
+
+
+
+ )}
+
+
+
+ {showAddMemoryView && (
+
setShowAddMemoryView(false)}
+ />
+ )}
+
)
-}
\ No newline at end of file
+}
diff --git a/apps/web/components/model-selector.tsx b/apps/web/components/model-selector.tsx
index d0e299743..f71e7d588 100644
--- a/apps/web/components/model-selector.tsx
+++ b/apps/web/components/model-selector.tsx
@@ -4,26 +4,7 @@ import { useState } from "react"
import { Button } from "@repo/ui/components/button"
import { ChevronDown } from "lucide-react"
import { motion } from "motion/react"
-
-const models = [
- {
- id: "gpt-5",
- name: "GPT 5",
- description: "OpenAI's latest model",
- },
- {
- id: "claude-sonnet-4.5",
- name: "Claude Sonnet 4.5",
- description: "Anthropic's advanced model",
- },
- {
- id: "gemini-2.5-pro",
- name: "Gemini 2.5 Pro",
- description: "Google's most capable model",
- },
-] as const
-
-type ModelId = (typeof models)[number]["id"]
+import { models, type ModelId, ModelIcon } from "@/lib/models"
interface ModelSelectorProps {
selectedModel?: ModelId
@@ -54,28 +35,7 @@ export function ModelSelector({
onClick={() => !disabled && setIsOpen(!isOpen)}
disabled={disabled}
>
-
+
{currentModel.name}
diff --git a/apps/web/components/views/chat/chat-messages.tsx b/apps/web/components/views/chat/chat-messages.tsx
index 22bc8a2a7..0c795ec08 100644
--- a/apps/web/components/views/chat/chat-messages.tsx
+++ b/apps/web/components/views/chat/chat-messages.tsx
@@ -1,6 +1,6 @@
"use client"
-import { useChat, useCompletion } from "@ai-sdk/react"
+import { useChat, useCompletion, type UIMessage } from "@ai-sdk/react"
import { cn } from "@lib/utils"
import { Button } from "@ui/components/button"
import { DefaultChatTransport } from "ai"
@@ -19,7 +19,9 @@ 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 { Spinner } from "../../spinner"
+import { areUIMessageArraysEqual } from "@/stores/chat"
interface MemoryResult {
documentId?: string
@@ -242,17 +244,14 @@ export function ChatMessages() {
const [input, setInput] = useState("")
const [selectedModel, setSelectedModel] = useState<
"gpt-5" | "claude-sonnet-4.5" | "gemini-2.5-pro"
- >(
- (sessionStorage.getItem(storageKey) as
- | "gpt-5"
- | "claude-sonnet-4.5"
- | "gemini-2.5-pro") ||
- "gemini-2.5-pro" ||
- "gemini-2.5-pro",
- )
+ >("gemini-2.5-pro")
const activeChatIdRef = useRef
(null)
const shouldGenerateTitleRef = useRef(false)
const hasRunInitialMessageRef = useRef(false)
+ const lastSavedMessagesRef = useRef(null)
+ const lastSavedActiveIdRef = useRef(null)
+ const lastLoadedChatIdRef = useRef(null)
+ const lastLoadedMessagesRef = useRef(null)
const { setDocumentIds } = useGraphHighlights()
@@ -288,11 +287,16 @@ export function ChatMessages() {
},
})
+ useEffect(() => {
+ lastLoadedMessagesRef.current = messages
+ }, [messages])
+
useEffect(() => {
activeChatIdRef.current = currentChatId ?? id ?? null
}, [currentChatId, id])
useEffect(() => {
+ if (typeof window === "undefined") return
if (currentChatId) {
const savedModel = sessionStorage.getItem(storageKey) as
| "gpt-5"
@@ -309,6 +313,7 @@ export function ChatMessages() {
}, [currentChatId, storageKey])
useEffect(() => {
+ if (typeof window === "undefined") return
if (currentChatId && !hasRunInitialMessageRef.current) {
// Check if there's an initial message from the home page in sessionStorage
const storageKey = `chat-initial-${currentChatId}`
@@ -330,20 +335,56 @@ export function ChatMessages() {
}, [id, currentChatId, setCurrentChatId])
useEffect(() => {
+ if (currentChatId !== lastLoadedChatIdRef.current) {
+ lastLoadedMessagesRef.current = null
+ lastSavedMessagesRef.current = null
+ }
+
+ if (currentChatId === lastLoadedChatIdRef.current) {
+ setInput("")
+ return
+ }
+
const msgs = getCurrentConversation()
+
if (msgs && msgs.length > 0) {
- setMessages(msgs)
+ const currentMessages = lastLoadedMessagesRef.current
+ if (!currentMessages || !areUIMessageArraysEqual(currentMessages, msgs)) {
+ lastLoadedMessagesRef.current = msgs
+ setMessages(msgs)
+ }
} else if (!currentChatId) {
- setMessages([])
+ if (
+ lastLoadedMessagesRef.current &&
+ lastLoadedMessagesRef.current.length > 0
+ ) {
+ lastLoadedMessagesRef.current = []
+ setMessages([])
+ }
}
+
+ lastLoadedChatIdRef.current = currentChatId
setInput("")
}, [currentChatId, getCurrentConversation, setMessages])
useEffect(() => {
const activeId = currentChatId ?? id
- if (activeId && messages.length > 0) {
- setConversation(activeId, messages)
+ if (!activeId || messages.length === 0) {
+ return
}
+
+ if (activeId !== lastSavedActiveIdRef.current) {
+ lastSavedMessagesRef.current = null
+ lastSavedActiveIdRef.current = activeId
+ }
+
+ const lastSaved = lastSavedMessagesRef.current
+ if (lastSaved && areUIMessageArraysEqual(lastSaved, messages)) {
+ return
+ }
+
+ lastSavedMessagesRef.current = messages
+ setConversation(activeId, messages)
}, [messages, currentChatId, id, setConversation])
const { complete } = useCompletion({
@@ -637,7 +678,17 @@ export function ChatMessages() {
className="w-full text-foreground placeholder:text-muted-foreground rounded-md outline-none resize-none text-base leading-relaxed px-3 py-3 bg-transparent"
rows={3}
/>
-
+
+
+
+
+ {modelNames[selectedModel]}
+
+