diff --git a/ui/web/src/lib/constants.ts b/ui/web/src/lib/constants.ts index 625e3d1f..e8c02198 100644 --- a/ui/web/src/lib/constants.ts +++ b/ui/web/src/lib/constants.ts @@ -1,7 +1,8 @@ export const ROUTES = { LOGIN: "/login", OVERVIEW: "/overview", - CHAT: "/chat/:sessionKey?", + CHAT: "/chat", + CHAT_PATTERN: "/chat/:sessionKey?", AGENTS: "/agents", AGENT_DETAIL: "/agents/:id", SESSIONS: "/sessions", diff --git a/ui/web/src/pages/chat/chat-page.tsx b/ui/web/src/pages/chat/chat-page.tsx index 82582ce6..59d7f166 100644 --- a/ui/web/src/pages/chat/chat-page.tsx +++ b/ui/web/src/pages/chat/chat-page.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback, useEffect, useRef } from "react"; +import { useState, useCallback, useEffect, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useParams, useNavigate } from "react-router"; import { Eye, PanelLeftOpen } from "lucide-react"; @@ -29,13 +29,17 @@ export function ChatPage() { // sessionKey derived from URL — single source of truth, no separate state const sessionKey = urlSessionKey ?? ""; - const [agentId, setAgentId] = useState(() => { + // Fallback agent ID used only when URL has no session key + const [agentIdFallback, setAgentIdFallback] = useState("default"); + + // Derive agentId from URL (source of truth), fallback to state when no session + const agentId = useMemo(() => { if (urlSessionKey) { const { agentId: parsed } = parseSessionKey(urlSessionKey); if (parsed) return parsed; } - return "default"; - }); + return agentIdFallback; + }, [urlSessionKey, agentIdFallback]); const { sessions, @@ -91,12 +95,10 @@ export function ChatPage() { const handleSessionSelect = useCallback( (key: string) => { const { agentId: parsed } = parseSessionKey(key); - if (parsed && parsed !== agentId) { - setAgentId(parsed); - } + if (parsed) setAgentIdFallback(parsed); navigate(`/chat/${encodeURIComponent(key)}`); }, - [navigate, agentId], + [navigate], ); const handleDeleteSession = useCallback(async (key: string) => { @@ -113,10 +115,12 @@ export function ChatPage() { const handleAgentChange = useCallback( (newAgentId: string) => { - setAgentId(newAgentId); - navigate(`/chat/${encodeURIComponent(`agent:${newAgentId}:ws:direct:${crypto.randomUUID()}`)}`); + setAgentIdFallback(newAgentId); + if (sessionKey) { + navigate("/chat"); + } }, - [navigate], + [navigate, sessionKey], ); const handleSend = useCallback( diff --git a/ui/web/src/pages/chat/hooks/use-chat-messages.ts b/ui/web/src/pages/chat/hooks/use-chat-messages.ts index 58453784..bb3f36a1 100644 --- a/ui/web/src/pages/chat/hooks/use-chat-messages.ts +++ b/ui/web/src/pages/chat/hooks/use-chat-messages.ts @@ -61,6 +61,11 @@ export function useChatMessages(sessionKey: string, agentId: string) { blockRepliesRef.current = []; cancelAnimationFrame(rafHandleRef.current); rafPendingRef.current = false; + // Clear messages when navigating away from a session (empty key). + // When switching to another session, loadHistory() will replace them. + if (!sessionKey) { + setMessages([]); + } }, [sessionKey]); // Load history (no loading spinner — the empty state placeholder is shown instead) diff --git a/ui/web/src/pages/providers/provider-list-row.tsx b/ui/web/src/pages/providers/provider-list-row.tsx index 52c9144d..683fa5e2 100644 --- a/ui/web/src/pages/providers/provider-list-row.tsx +++ b/ui/web/src/pages/providers/provider-list-row.tsx @@ -17,9 +17,11 @@ export function ProviderListRow({ provider, onClick, onDelete }: ProviderListRow const tb = PROVIDER_TYPE_BADGE[provider.provider_type] ?? { label: provider.provider_type, variant: "outline" as const }; return ( - )} - + ); } diff --git a/ui/web/src/routes.tsx b/ui/web/src/routes.tsx index bdde235c..af7afa1e 100644 --- a/ui/web/src/routes.tsx +++ b/ui/web/src/routes.tsx @@ -146,7 +146,7 @@ export function AppRoutes() { > } /> } /> - } /> + } /> } /> } /> } />