import { useState, useMemo, useCallback } from 'react'; import type { Message } from './use-socket.js'; export interface SearchMatch { messageIndex: number; charOffset: number; } export interface UseSearchReturn { query: string; setQuery: (q: string) => void; matches: SearchMatch[]; currentMatchIndex: number; nextMatch: () => void; prevMatch: () => void; clear: () => void; totalMatches: number; } export function useSearch(messages: Message[]): UseSearchReturn { const [query, setQuery] = useState(''); const [currentMatchIndex, setCurrentMatchIndex] = useState(0); const matches = useMemo(() => { if (query.length < 2) return []; const lowerQuery = query.toLowerCase(); const result: SearchMatch[] = []; for (let i = 0; i < messages.length; i++) { const msg = messages[i]; if (!msg) continue; const content = msg.content.toLowerCase(); let offset = 0; while (true) { const idx = content.indexOf(lowerQuery, offset); if (idx === -1) break; result.push({ messageIndex: i, charOffset: idx }); offset = idx + 1; } } return result; }, [query, messages]); // Reset match index when matches change useMemo(() => { setCurrentMatchIndex(0); }, [matches]); const nextMatch = useCallback(() => { if (matches.length === 0) return; setCurrentMatchIndex((prev) => (prev + 1) % matches.length); }, [matches.length]); const prevMatch = useCallback(() => { if (matches.length === 0) return; setCurrentMatchIndex((prev) => (prev - 1 + matches.length) % matches.length); }, [matches.length]); const clear = useCallback(() => { setQuery(''); setCurrentMatchIndex(0); }, []); return { query, setQuery, matches, currentMatchIndex, nextMatch, prevMatch, clear, totalMatches: matches.length, }; }