diff --git a/src/pages/Chat/ChatMessage.tsx b/src/pages/Chat/ChatMessage.tsx index 8de44b0e2..416bcd6b6 100644 --- a/src/pages/Chat/ChatMessage.tsx +++ b/src/pages/Chat/ChatMessage.tsx @@ -346,9 +346,9 @@ function MessageBubble({ )} > {isUser ? ( -

{text}

+

{text}

) : ( -
+
+ {children} ); @@ -372,7 +372,7 @@ function MessageBubble({ }, a({ href, children }) { return ( - + {children} ); diff --git a/src/pages/Chat/index.tsx b/src/pages/Chat/index.tsx index 4a2c65d2a..cfc2e99fc 100644 --- a/src/pages/Chat/index.tsx +++ b/src/pages/Chat/index.tsx @@ -35,6 +35,8 @@ export function Chat() { const abortRun = useChatStore((s) => s.abortRun); const clearError = useChatStore((s) => s.clearError); + const cleanupEmptySession = useChatStore((s) => s.cleanupEmptySession); + const messagesEndRef = useRef(null); const [streamingTimestamp, setStreamingTimestamp] = useState(0); @@ -54,8 +56,11 @@ export function Chat() { })(); return () => { cancelled = true; + // If the user navigates away without sending any messages, remove the + // empty session so it doesn't linger as a ghost entry in the sidebar. + cleanupEmptySession(); }; - }, [isGatewayRunning, loadHistory, loadSessions]); + }, [isGatewayRunning, loadHistory, loadSessions, cleanupEmptySession]); // Auto-scroll on new messages, streaming, or activity changes useEffect(() => { diff --git a/src/stores/chat.ts b/src/stores/chat.ts index 62ebf1e62..54c14cac5 100644 --- a/src/stores/chat.ts +++ b/src/stores/chat.ts @@ -99,6 +99,7 @@ interface ChatState { switchSession: (key: string) => void; newSession: () => void; deleteSession: (key: string) => Promise; + cleanupEmptySession: () => void; loadHistory: (quiet?: boolean) => Promise; sendMessage: (text: string, attachments?: Array<{ fileName: string; mimeType: string; fileSize: number; stagedPath: string; preview: string | null }>) => Promise; abortRun: () => Promise; @@ -973,8 +974,11 @@ export const useChatStore = create((set, get) => ({ } } if (!dedupedSessions.find((s) => s.key === nextSessionKey) && dedupedSessions.length > 0) { - // Current session not found at all — switch to the first available session - nextSessionKey = dedupedSessions[0].key; + // Current session not found in the backend list + const isNewEmptySession = get().messages.length === 0; + if (!isNewEmptySession) { + nextSessionKey = dedupedSessions[0].key; + } } const sessionsWithCurrent = !dedupedSessions.find((s) => s.key === nextSessionKey) && nextSessionKey @@ -1153,6 +1157,27 @@ export const useChatStore = create((set, get) => ({ })); }, + // ── Cleanup empty session on navigate away ── + + cleanupEmptySession: () => { + const { currentSessionKey, messages } = get(); + // Only remove non-main sessions that were never used (no messages sent). + // This mirrors the "leavingEmpty" logic in switchSession so that creating + // a new session and immediately navigating away doesn't leave a ghost entry + // in the sidebar. + const isEmptyNonMain = !currentSessionKey.endsWith(':main') && messages.length === 0; + if (!isEmptyNonMain) return; + set((s) => ({ + sessions: s.sessions.filter((sess) => sess.key !== currentSessionKey), + sessionLabels: Object.fromEntries( + Object.entries(s.sessionLabels).filter(([k]) => k !== currentSessionKey), + ), + sessionLastActivity: Object.fromEntries( + Object.entries(s.sessionLastActivity).filter(([k]) => k !== currentSessionKey), + ), + })); + }, + // ── Load chat history ── loadHistory: async (quiet = false) => {