From 11d6f14645a9b950773676cc128be7f10aabd452 Mon Sep 17 00:00:00 2001 From: Mark Henderson Date: Mon, 2 Mar 2026 17:02:13 -0800 Subject: [PATCH] Fix Atlas chat duplicate messages; add Reset button - Add cleanup flag to useEffect to prevent state updates after unmount, eliminating the race condition on rapid navigation - Add handleReset: calls DELETE endpoint, clears state, re-triggers greeting - Add subtle "Reset" button (top-right of message area) so user can wipe polluted history and start fresh Made-with: Cursor --- components/AtlasChat.tsx | 49 ++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/components/AtlasChat.tsx b/components/AtlasChat.tsx index 8154fc9..a4f9af9 100644 --- a/components/AtlasChat.tsx +++ b/components/AtlasChat.tsx @@ -147,34 +147,50 @@ export function AtlasChat({ projectId }: AtlasChatProps) { } }, [projectId]); - // On mount: load stored history; if empty, trigger Atlas greeting + // On mount: load stored history; if empty, trigger Atlas greeting exactly once useEffect(() => { - if (historyLoaded) return; + let cancelled = false; // guard against unmount during fetch fetch(`/api/projects/${projectId}/atlas-chat`) .then(r => r.json()) .then((data: { messages: ChatMessage[] }) => { + if (cancelled) return; const stored = data.messages ?? []; setMessages(stored); setHistoryLoaded(true); - // Only trigger greeting if there's genuinely no history yet + // Only greet if there is genuinely no history and we haven't triggered yet if (stored.length === 0 && !initTriggered.current) { initTriggered.current = true; sendToAtlas("__atlas_init__", true); } }) .catch(() => { + if (cancelled) return; setHistoryLoaded(true); - // If we can't load, still try to greet on first open - if (!initTriggered.current) { - initTriggered.current = true; - sendToAtlas("__atlas_init__", true); - } }); + + return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [projectId]); + const handleReset = async () => { + if (!confirm("Clear this conversation and start fresh?")) return; + try { + await fetch(`/api/projects/${projectId}/atlas-chat`, { method: "DELETE" }); + setMessages([]); + setHistoryLoaded(false); + initTriggered.current = false; + // Trigger fresh greeting + setTimeout(() => { + initTriggered.current = true; + sendToAtlas("__atlas_init__", true); + }, 100); + } catch { + // swallow + } + }; + const handleSend = () => { const text = input.trim(); if (!text || isStreaming) return; @@ -226,7 +242,22 @@ export function AtlasChat({ projectId }: AtlasChatProps) { {/* Messages */} {!isEmpty && ( -
+
+ {/* Reset button — top right, only visible on hover of the area */} + {messages.map((msg, i) => ( ))}