| import { useState } from 'react'; | |
| interface ResultCardProps { | |
| title: string; | |
| score: number; | |
| snippet: string; | |
| expanded?: boolean; | |
| onToggle?: () => void; | |
| } | |
| function ScoreBadge({ score }: { score: number }) { | |
| const pct = Math.round(score * 100); | |
| const bg = pct >= 80 ? 'var(--score-good-bg)' : pct >= 50 ? 'var(--score-mid-bg)' : 'var(--score-bad-bg)'; | |
| const color = pct >= 80 ? '#2e7d32' : pct >= 50 ? '#f57f17' : 'var(--text-secondary)'; | |
| return ( | |
| <span style={{ | |
| display: 'inline-block', | |
| padding: '0.15rem 0.45rem', | |
| borderRadius: '4px', | |
| background: bg, | |
| color, | |
| fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace", | |
| fontSize: '0.72rem', | |
| fontWeight: 700, | |
| }}> | |
| {pct}% | |
| </span> | |
| ); | |
| } | |
| export default function ResultCard({ title, score, snippet, expanded: expandedProp, onToggle }: ResultCardProps) { | |
| const [localExpanded, setLocalExpanded] = useState(false); | |
| const isControlled = expandedProp !== undefined; | |
| const expanded = isControlled ? expandedProp : localExpanded; | |
| function handleToggle() { | |
| if (isControlled) { | |
| onToggle?.(); | |
| } else { | |
| setLocalExpanded(e => !e); | |
| } | |
| } | |
| const preview = snippet.length > 200 ? snippet.slice(0, 200) + '\u2026' : snippet; | |
| return ( | |
| <div | |
| onClick={handleToggle} | |
| style={{ | |
| padding: '0.65rem 0.85rem', | |
| background: 'var(--bg-card)', | |
| border: '1px solid var(--border)', | |
| borderRadius: '6px', | |
| marginBottom: '0.4rem', | |
| cursor: 'pointer', | |
| transition: 'box-shadow 0.15s', | |
| }} | |
| onMouseEnter={e => { (e.currentTarget as HTMLDivElement).style.boxShadow = '0 2px 8px var(--shadow)'; }} | |
| onMouseLeave={e => { (e.currentTarget as HTMLDivElement).style.boxShadow = 'none'; }} | |
| > | |
| <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '0.5rem' }}> | |
| <span style={{ | |
| fontFamily: 'system-ui, -apple-system, sans-serif', | |
| fontSize: '0.85rem', | |
| fontWeight: 600, | |
| color: 'var(--text)', | |
| overflow: 'hidden', | |
| textOverflow: 'ellipsis', | |
| whiteSpace: 'nowrap', | |
| flex: 1, | |
| }}> | |
| {title} | |
| </span> | |
| <ScoreBadge score={score} /> | |
| <span style={{ color: 'var(--text-muted)', fontSize: '0.75rem', flexShrink: 0 }}> | |
| {expanded ? '\u25B2' : '\u25BC'} | |
| </span> | |
| </div> | |
| <div style={{ | |
| marginTop: '0.4rem', | |
| fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', monospace", | |
| fontSize: '0.72rem', | |
| color: 'var(--text-secondary)', | |
| lineHeight: 1.5, | |
| whiteSpace: expanded ? 'pre-wrap' : 'nowrap', | |
| overflow: 'hidden', | |
| textOverflow: expanded ? 'unset' : 'ellipsis', | |
| }}> | |
| {expanded ? snippet : preview} | |
| </div> | |
| </div> | |
| ); | |
| } | |