src/components/search/search-results.tsximport { Fragment } from 'react'import Link from 'next/link'import Highlighter from 'react-highlight-words'import {type AutocompleteApi,type AutocompleteCollection,} from '@algolia/autocomplete-core'import { type Result } from './search-client'/*** Extract context around the search query from content* @param content - The full content text* @param query - The search query* @param contextLength - Number of characters to show on each side* @returns Excerpt with query in context, or beginning of content*/function getContextExcerpt(content: string,query: string,contextLength: number = 60): string {if (!content || !query) return content?.slice(0, 120) || ''// Find the query position (case-insensitive)const lowerContent = content.toLowerCase()const lowerQuery = query.toLowerCase()const index = lowerContent.indexOf(lowerQuery)if (index === -1) {// Query not found, return beginningreturn content.slice(0, 120)}// Calculate start and end positionsconst start = Math.max(0, index - contextLength)const end = Math.min(content.length, index + query.length + contextLength)// Extract excerptlet excerpt = content.slice(start, end)// Add ellipsis if neededif (start > 0) excerpt = '...' + excerptif (end < content.length) excerpt = excerpt + '...'return excerpt}/*** Highlight matching text in a string using react-highlight-words*/function HighlightQuery({ text, query }: { text: string; query: string }) {if (!query) return <>{text}</>return (<HighlightersearchWords={[query]}autoEscape={true}textToHighlight={text}highlightClassName="font-bold text-contrast bg-yellow-200"/>)}type Autocomplete = AutocompleteApi<Result,React.SyntheticEvent,React.MouseEvent,React.KeyboardEvent>export function SearchResults({autocomplete,query,collection,onResultClick,}: {autocomplete: Autocompletequery: stringcollection: AutocompleteCollection<Result>onResultClick: () => void}) {if (!collection || collection.items.length === 0) {return (<div className="px-4 py-10 bg-body2 text-contrast text-center">No results for “{query}”</div>)}if (collection.items.length === 0) {return (<p className="px-4 py-8 text-center text-contrast">No results for “<span className="break-words text-contrast">{query}</span>”</p>)}return (<ul {...autocomplete.getListProps()}>{collection.items.map((result) => {const href = result.urlconst excerpt = getContextExcerpt(result.content || '', query, 60)// Build breadcrumb hierarchy: Always show pageTitle, then title if it's different (subheading)const hierarchy: string[] = []if (result.pageTitle) {hierarchy.push(result.pageTitle)// Only add title as subheading if it's different from pageTitleif (result.title && result.title !== result.pageTitle) {hierarchy.push(result.title)}} else if (result.title) {// Fallback: if no pageTitle, just show titlehierarchy.push(result.title)}return (<likey={href}className="group block cursor-default rounded-md px-3 py-2 aria-selected:bg-accent4"{...autocomplete.getItemProps({item: result,source: collection.source,})}><Linkhref={href}className="block"onClick={onResultClick}>{/* Context excerpt with highlighted query */}<div className="text-contrast group-aria-selected:text-contrast text-sm line-clamp-2"><HighlightQuerytext={excerpt}query={query}/></div>{/* Breadcrumb navigation: pageTitle / heading */}{hierarchy.length > 0 && (<div className="mt-1 text-xs text-contrast/30">{hierarchy.map((item: string, itemIndex: number, items: string[]) => (<Fragment key={itemIndex}>{item}{itemIndex < items.length - 1 && (<span className="mx-1">/</span>)}</Fragment>))}</div>)}</Link></li>)})}</ul>)}