import { useState } from "react"; import { Search } from "lucide-react"; import { useChatSearch } from "../hooks/useChatSearch"; import { CommandDialog, CommandEmpty, CommandInput, CommandItem, CommandList, } from "@/components/ui/command"; import { Command } from "cmdk"; interface ChatSearchDialogProps { open: boolean; onOpenChange: (open: boolean) => void; companyId: string | null; onNavigate: (conversationId: string, messageId: string) => void; } function stripMarkdown(text: string): string { return text .replace(/```[\s\S]*?```/g, "") .replace(/`[^`]+`/g, "") .replace(/\*\*([^*]+)\*\*/g, "$1") .replace(/\*([^*]+)\*/g, "$1") .replace(/#{1,6}\s/g, "") .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") .replace(/>\s/g, "") .trim(); } /** Split text into segments, marking portions that match the query terms */ function splitWithHighlight(text: string, query: string): Array<{ text: string; highlight: boolean }> { if (!query.trim()) return [{ text, highlight: false }]; const terms = query.trim().split(/\s+/).filter(Boolean); const pattern = terms.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"); const re = new RegExp(`(${pattern})`, "gi"); const parts = text.split(re); return parts.map((part) => ({ text: part, highlight: re.test(part), })); } function HighlightedText({ text, query }: { text: string; query: string }) { const segments = splitWithHighlight(text, query); return ( <> {segments.map((seg, i) => seg.highlight ? ( {seg.text} ) : ( {seg.text} ), )} > ); } function formatRelativeTime(dateStr: string): string { const date = new Date(dateStr); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMin = Math.floor(diffMs / 60000); if (diffMin < 1) return "just now"; if (diffMin < 60) return `${diffMin}m ago`; const diffHr = Math.floor(diffMin / 60); if (diffHr < 24) return `${diffHr}h ago`; const diffDays = Math.floor(diffHr / 24); if (diffDays < 30) return `${diffDays}d ago`; return date.toLocaleDateString(); } export function ChatSearchDialog({ open, onOpenChange, companyId, onNavigate }: ChatSearchDialogProps) { const [query, setQuery] = useState(""); const { data } = useChatSearch(companyId, query); const results = data?.items ?? []; function handleSelect(conversationId: string, messageId: string) { onNavigate(conversationId, messageId); onOpenChange(false); setQuery(""); } return ( { onOpenChange(v); if (!v) setQuery(""); }} title="Search messages" description="Search all messages across conversations" > {query.trim().length >= 2 && results.length === 0 && ( No results found. )} {results.map((result) => { const snippet = stripMarkdown(result.content).slice(0, 120); return ( handleSelect(result.conversationId, result.messageId)} className="flex flex-col items-start gap-0.5 py-2" > {result.conversationTitle ?? "Untitled conversation"} {result.role} {formatRelativeTime(result.createdAt)} ); })} ); }