From b439e7ea0182927aa14ad565374baef316f9b765 Mon Sep 17 00:00:00 2001 From: Prasanna721 <106952318+Prasanna721@users.noreply.github.com> Date: Wed, 11 Feb 2026 00:25:35 +0000 Subject: [PATCH] fix: chat messages saving broken (#730) 1. New chat messages saving to old thread (race condition) 2. Feedback modal null handling issue (boolean | null not coerced to boolean) 3. Wrong icon on Integrations tab (was Cable, now Sun) 4. Wrong icon on Graph tab (was LayoutGridIcon, now GraphIcon) 5. Missing cursor pointer on header tabs 6. Default view was "graph" instead of "list" --- apps/web/app/(app)/settings/page.tsx | 19 +-- apps/web/components/new/chat/index.tsx | 55 ++++++-- apps/web/components/new/header.tsx | 37 ++--- apps/web/components/new/integration-icons.tsx | 3 + apps/web/components/new/integrations-view.tsx | 128 +++++++++--------- apps/web/lib/search-params.ts | 6 +- packages/ui/components/dialog.tsx | 2 +- 7 files changed, 136 insertions(+), 114 deletions(-) diff --git a/apps/web/app/(app)/settings/page.tsx b/apps/web/app/(app)/settings/page.tsx index 35f4fb609..179c03012 100644 --- a/apps/web/app/(app)/settings/page.tsx +++ b/apps/web/app/(app)/settings/page.tsx @@ -14,6 +14,7 @@ import Support from "@/components/new/settings/support" import { useRouter } from "next/navigation" import { useIsMobile } from "@hooks/use-mobile" import { analytics } from "@/lib/analytics" +import { Sun } from "lucide-react" const TABS = ["account", "integrations", "connections", "support"] as const type SettingsTab = (typeof TABS)[number] @@ -52,23 +53,7 @@ const NAV_ITEMS: NavItem[] = [ id: "integrations", label: "Integrations", description: "Save, sync and search memories across tools", - icon: ( - - ), + icon: , }, { id: "connections", diff --git a/apps/web/components/new/chat/index.tsx b/apps/web/components/new/chat/index.tsx index 657da9455..425dbc322 100644 --- a/apps/web/components/new/chat/index.tsx +++ b/apps/web/components/new/chat/index.tsx @@ -138,16 +138,36 @@ export function ChatSidebar({ ) const pendingFollowUpGenerations = useRef>(new Set()) const messagesContainerRef = useRef(null) + const sentQueuedMessageRef = useRef(null) const { selectedProject } = useProject() const { viewMode } = useViewMode() const { user } = useAuth() const [threadId, setThreadId] = useQueryState("thread", threadParam) - const fallbackChatId = useMemo(() => generateId(), []) + const [fallbackChatId, setFallbackChatId] = useState(() => generateId()) const currentChatId = threadId ?? fallbackChatId + const chatIdRef = useRef(currentChatId) + chatIdRef.current = currentChatId const setCurrentChatId = useCallback( (id: string) => setThreadId(id), [setThreadId], ) + const chatTransport = useMemo( + () => + new DefaultChatTransport({ + api: `${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/v2`, + credentials: "include", + prepareSendMessagesRequest: ({ messages }) => ({ + body: { + messages, + metadata: { + chatId: chatIdRef.current, + projectId: selectedProject, + }, + }, + }), + }), + [currentChatId, selectedProject], + ) const [pendingThreadLoad, setPendingThreadLoad] = useState<{ id: string messages: UIMessage[] @@ -174,10 +194,7 @@ export function ChatSidebar({ const { messages, sendMessage, status, setMessages, stop } = useChat({ id: currentChatId ?? undefined, - transport: new DefaultChatTransport({ - api: `${process.env.NEXT_PUBLIC_BACKEND_URL}/chat/v2`, - credentials: "include", - }), + transport: chatTransport, onFinish: async (result) => { if (result.message.role !== "assistant") return @@ -376,9 +393,13 @@ export function ChatSidebar({ const handleNewChat = useCallback(() => { analytics.newChatCreated() + const newChatId = generateId() + chatIdRef.current = newChatId + setMessages([]) setThreadId(null) + setFallbackChatId(newChatId) setInput("") - }, [setThreadId]) + }, [setThreadId, setMessages]) const fetchThreads = useCallback(async () => { setIsLoadingThreads(true) @@ -492,14 +513,23 @@ export function ChatSidebar({ isChatOpen && queuedMessage && status !== "submitted" && - status !== "streaming" + status !== "streaming" && + sentQueuedMessageRef.current !== queuedMessage ) { + sentQueuedMessageRef.current = queuedMessage analytics.chatMessageSent({ source: "highlight" }) sendMessage({ text: queuedMessage }) onConsumeQueuedMessage?.() } }, [isChatOpen, queuedMessage, status, sendMessage, onConsumeQueuedMessage]) + // Reset the sent message ref when queued message is consumed + useEffect(() => { + if (!queuedMessage) { + sentQueuedMessageRef.current = null + } + }, [queuedMessage]) + // Scroll to bottom when a new user message is added useEffect(() => { const lastMessage = messages[messages.length - 1] @@ -867,12 +897,11 @@ export function ChatSidebar({ )} ))} - {(status === "submitted" || status === "streaming") && - messages[messages.length - 1]?.role === "user" && ( -
- -
- )} + {(status === "submitted" || status === "streaming") && ( +
+ +
+ )} diff --git a/apps/web/components/new/header.tsx b/apps/web/components/new/header.tsx index be6716b87..efc952a53 100644 --- a/apps/web/components/new/header.tsx +++ b/apps/web/components/new/header.tsx @@ -11,7 +11,7 @@ import { Settings, Home, Code2, - Cable, + Sun, ExternalLink, HelpCircle, MenuIcon, @@ -22,6 +22,7 @@ import { Button } from "@ui/components/button" import { cn } from "@lib/utils" import { dmSansClassName } from "@/lib/fonts" import { Tabs, TabsList, TabsTrigger } from "@ui/components/tabs" +import { GraphIcon } from "@/components/new/integration-icons" import { DropdownMenu, DropdownMenuContent, @@ -48,18 +49,18 @@ interface HeaderProps { onOpenSearch?: () => void } -export function Header({ - onAddMemory, - onOpenChat, - onOpenSearch, -}: HeaderProps) { +export function Header({ onAddMemory, onOpenChat, onOpenSearch }: HeaderProps) { const { user } = useAuth() const { selectedProject } = useProject() const { switchProject } = useProjectMutations() const router = useRouter() const isMobile = useIsMobile() const { resetOrgOnboarded } = useOrgOnboarding() - const [isFeedbackOpen, setIsFeedbackOpen] = useQueryState("feedback", feedbackParam) + const [feedbackOpen, setFeedbackOpen] = useQueryState( + "feedback", + feedbackParam, + ) + const isFeedbackOpen = feedbackOpen ?? false const { viewMode, setViewMode } = useViewMode() const handleTryOnboarding = () => { @@ -67,9 +68,7 @@ export function Header({ router.push("/onboarding?step=input&flow=welcome") } - const handleFeedback = () => { - setIsFeedbackOpen(true) - } + const handleFeedback = () => setFeedbackOpen(true) const displayName = user?.displayUsername || @@ -155,13 +154,15 @@ export function Header({ {!isMobile && ( setViewMode(v === "grid" ? "list" : v as "graph" | "integrations")} + onValueChange={(v) => + setViewMode(v === "grid" ? "list" : (v as "graph" | "integrations")) + } > @@ -171,21 +172,21 @@ export function Header({ - + Graph - + Integrations @@ -232,7 +233,7 @@ export function Header({ onClick={() => setViewMode("integrations")} className="px-3 py-2.5 rounded-md hover:bg-[#293952]/40 cursor-pointer text-white text-sm font-medium gap-2" > - + Integrations setIsFeedbackOpen(false)} + onClose={() => setFeedbackOpen(false)} /> ) diff --git a/apps/web/components/new/integration-icons.tsx b/apps/web/components/new/integration-icons.tsx index 54567db2e..4603346c4 100644 --- a/apps/web/components/new/integration-icons.tsx +++ b/apps/web/components/new/integration-icons.tsx @@ -1,4 +1,7 @@ import Image from "next/image" +import { Rotate3d } from "lucide-react" + +export { Rotate3d as GraphIcon } export function ChromeIcon({ className }: { className?: string }) { return ( diff --git a/apps/web/components/new/integrations-view.tsx b/apps/web/components/new/integrations-view.tsx index 5b798c2b9..507244b57 100644 --- a/apps/web/components/new/integrations-view.tsx +++ b/apps/web/components/new/integrations-view.tsx @@ -2,7 +2,7 @@ import { useState } from "react" import { cn } from "@lib/utils" -import { dmSansClassName, dmSans125ClassName } from "@/lib/fonts" +import { dmSansClassName } from "@/lib/fonts" import { Button } from "@ui/components/button" import { MCPDetailView } from "@/components/new/mcp-modal/mcp-detail-view" import { XBookmarksDetailView } from "@/components/new/onboarding/x-bookmarks-detail-view" @@ -17,7 +17,7 @@ import { RaycastIcon, } from "@/components/new/integration-icons" import { GoogleDrive, Notion, OneDrive } from "@ui/assets/icons" -import { Cable, ArrowLeft } from "lucide-react" +import { ArrowLeft, Sun } from "lucide-react" import Image from "next/image" type CardId = @@ -45,9 +45,27 @@ const cards: IntegrationCardDef[] = [ pro: true, icon: (
- Claude Code - OpenCode - ClawdBot + Claude Code + OpenCode + ClawdBot
), }, @@ -69,11 +87,7 @@ const cards: IntegrationCardDef[] = [ title: "Connect to AI", description: "Set up MCP to use your memory in Cursor, Claude, and more", icon: ( - MCP + MCP ), }, { @@ -98,9 +112,7 @@ const cards: IntegrationCardDef[] = [ id: "import", title: "Import Bookmarks", description: "Bring in X/Twitter bookmarks and turn them into memories", - icon: ( - X - ), + icon: X, }, ] @@ -131,46 +143,43 @@ function DetailWrapper({ export function IntegrationsView() { const [selectedCard, setSelectedCard] = useState(null) - if (selectedCard === "mcp") { - return setSelectedCard(null)} /> - } - if (selectedCard === "import") { - return setSelectedCard(null)} /> - } - if (selectedCard === "chrome") { - return ( - setSelectedCard(null)}> - - - ) - } - if (selectedCard === "shortcuts") { - return ( - setSelectedCard(null)}> - - - ) - } - if (selectedCard === "raycast") { - return ( - setSelectedCard(null)}> - - - ) - } - if (selectedCard === "connections") { - return ( - setSelectedCard(null)}> - - - ) - } - if (selectedCard === "plugins") { - return ( - setSelectedCard(null)}> - - - ) + const handleBack = () => setSelectedCard(null) + + switch (selectedCard) { + case "mcp": + return + case "import": + return + case "chrome": + return ( + + + + ) + case "shortcuts": + return ( + + + + ) + case "raycast": + return ( + + + + ) + case "connections": + return ( + + + + ) + case "plugins": + return ( + + + + ) } return ( @@ -178,15 +187,10 @@ export function IntegrationsView() {
- +

Integrations

-

+

Connect supermemory to your tools and workflows

@@ -215,9 +219,7 @@ export function IntegrationsView() { {card.icon}
-

- {card.title} -

+

{card.title}