function ResearchLoadingSkeleton({ exerciseName }) { return (
) } function ResearchError({ message, onDismiss }) { return (
{message} {onDismiss && ( )}
) } function ResearchSourceCard({ result, index }) { return (
  • {index + 1} {result.title} {result.snippet && (

    {result.snippet}

    )} {result.isFallback && ( Suggested )}
  • ) } function ResearchProviderBadge({ provider, status }) { if (!provider) return null; const badgeConfig = { exa: { emoji: '🔍', label: 'Exa Search', color: 'primary' }, fallback: { emoji: '🔗', label: 'Web Sources', color: 'secondary' }, gemini: { emoji: '✨', label: 'AI Summary', color: 'accent' }, openrouter: { emoji: '🤖', label: 'AI Powered', color: 'accent' } }; const config = badgeConfig[provider] || { emoji: '📊', label: provider, color: 'secondary' }; const isDegraded = status === 'degraded'; return (
    {config.label} {isDegraded && ( (Fallback) )}
    ); } /** * ResearchDisplay — pure presentational component. * * Props: * loading {boolean} Show loading skeleton * error {string} Error message to display * data {object} Research data: { summary, results, provider, status } * name {string} Exercise name (shown during loading) * onDismiss {function} Clear error callback */ function ResearchDisplay({ loading, error, data, name, onDismiss }) { if (loading) { return } if (error) { return } if (!data) return null const hasSummary = Boolean(data.summary) const hasSources = Array.isArray(data.results) && data.results.length > 0 return (
    {hasSummary && (

    Summary

    {data.summary}

    )}
    {data.provider && ( )}
    {hasSources && (

    Sources {data.results.length}

      {data.results.map((result, i) => ( ))}
    )} {!hasSummary && !hasSources && (

    No research data found for this exercise.

    )}
    ) } export default ResearchDisplay