From 4a23ab132690fdefc0b756eb7bf33eb7e2dbe206 Mon Sep 17 00:00:00 2001 From: MaheshtheDev <38828053+MaheshtheDev@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:29:39 +0000 Subject: [PATCH] feat: history view in the header and dialog modal (#456) --- apps/web/components/header.tsx | 145 +++++++++++++++++- .../components/views/chat/chat-messages.tsx | 13 +- 2 files changed, 150 insertions(+), 8 deletions(-) diff --git a/apps/web/components/header.tsx b/apps/web/components/header.tsx index 7e6c800a..23fb5cf3 100644 --- a/apps/web/components/header.tsx +++ b/apps/web/components/header.tsx @@ -12,6 +12,8 @@ import { LogOut, WaypointsIcon, Gauge, + HistoryIcon, + Trash2, } from "lucide-react" import { DropdownMenuContent, @@ -26,20 +28,57 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@ui/components/tooltip" import { useAuth } from "@lib/auth-context" import { ConnectAIModal } from "./connect-ai-modal" import { useTheme } from "next-themes" -import { cn } from "@lib/utils" import { usePathname, useRouter } from "next/navigation" import { MCPIcon } from "./menu" import { authClient } from "@lib/auth" import { analytics } from "@/lib/analytics" -import { useGraphModal, usePersistentChat } from "@/stores" +import { useGraphModal, usePersistentChat, useProject } from "@/stores" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@ui/components/dialog" +import { ScrollArea } from "@ui/components/scroll-area" +import { formatDistanceToNow } from "date-fns" +import { cn } from "@lib/utils" +import { useMemo, useState } from "react" export function Header({ onAddMemory }: { onAddMemory?: () => void }) { const { user } = useAuth() const { theme, setTheme } = useTheme() const router = useRouter() const { setIsOpen: setGraphModalOpen } = useGraphModal() - const { getCurrentChat } = usePersistentChat() + const { + getCurrentChat, + conversations, + currentChatId, + setCurrentChatId, + deleteConversation, + } = usePersistentChat() + const { selectedProject } = useProject() const pathname = usePathname() + const [isDialogOpen, setIsDialogOpen] = useState(false) + + const sorted = useMemo(() => { + return [...conversations].sort((a, b) => + a.lastUpdated < b.lastUpdated ? 1 : -1, + ) + }, [conversations]) + + function handleNewChat() { + analytics.newChatStarted() + const newId = crypto.randomUUID() + setCurrentChatId(newId) + router.push(`/chat/${newId}`) + setIsDialogOpen(false) + } + + function formatRelativeTime(isoString: string): string { + return formatDistanceToNow(new Date(isoString), { addSuffix: true }) + } const handleSignOut = () => { analytics.userSignedOut() @@ -87,6 +126,106 @@ export function Header({ onAddMemory }: { onAddMemory?: () => void }) { c + { + setIsDialogOpen(open) + if (open) { + analytics.chatHistoryViewed() + } + }} + > + + + + + + + + + + Chat History + + + + + Conversations + + Project{" "} + + {selectedProject} + + + + + + + {sorted.map((c) => { + const isActive = c.id === currentChatId + return ( + { + setCurrentChatId(c.id) + router.push(`/chat/${c.id}`) + setIsDialogOpen(false) + }} + className={cn( + "flex items-center justify-between rounded-md px-3 py-2 outline-none w-full text-left", + "transition-colors", + isActive ? "bg-primary/10" : "hover:bg-muted", + )} + aria-current={isActive ? "true" : undefined} + > + + + + {c.title || "Untitled Chat"} + + + + Last updated {formatRelativeTime(c.lastUpdated)} + + + { + e.stopPropagation() + analytics.chatDeleted() + deleteConversation(c.id) + }} + aria-label="Delete conversation" + > + + + + ) + })} + {sorted.length === 0 && ( + + No conversations yet + + )} + + + + New Conversation + + + ((sessionStorage.getItem(storageKey) as "gpt-5" | "claude-sonnet-4.5" | "gemini-2.5-pro" || "gemini-2.5-pro") || "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", + ) const activeChatIdRef = useRef(null) const shouldGenerateTitleRef = useRef(false) const hasRunInitialMessageRef = useRef(false) const { setDocumentIds } = useGraphHighlights() - console.log("selectedModel", selectedModel) - const { messages, sendMessage, status, stop, setMessages, id, regenerate } = useChat({ id: currentChatId ?? undefined, @@ -277,7 +281,6 @@ export function ChatMessages() { savedModel && ["gpt-5", "claude-sonnet-4.5", "gemini-2.5-pro"].includes(savedModel) ) { - console.log("savedModel", savedModel) setSelectedModel(savedModel) } }
Chat History