/** * Connection Path — presentational view. Pure: renders the shortest-path result * (or the prompt / no-path / same / missing states). No data fetching, no clock. */ import { Fragment } from 'react'; import { useT } from '../../lib/i18n/I18nContext'; import type { ConnectionPathResult } from '../../lib/memory/connectionPath'; import Button from '../ui/Button'; interface ConnectionPathPanelProps { result: ConnectionPathResult | null; hasGraph: boolean; loading?: boolean; error?: string | null; onRetry?: () => void; } const ConnectionPathPanel = ({ result, hasGraph, loading, error, onRetry, }: ConnectionPathPanelProps) => { const { t } = useT(); const intro = (

{t('connectionPath.title')}

{t('connectionPath.intro')}

); if (loading) { return (
{intro}
{[0, 1, 2].map(i => (
))}
); } if (error) { return (
{intro}

{t('connectionPath.errorPrefix')} {error}

{onRetry && ( )}
); } if (!hasGraph) { return (
{intro}

{t('connectionPath.empty')}

{t('connectionPath.emptyHint')}

); } if (!result) { return (
{intro}

{t('connectionPath.prompt')}

); } let message: string | null = null; if (result.reason === 'same') message = t('connectionPath.sameMessage'); else if (result.reason === 'missing-source') message = t('connectionPath.missingSource').replace('{entity}', result.source); else if (result.reason === 'missing-target') message = t('connectionPath.missingTarget').replace('{entity}', result.target); else if (result.reason === 'no-path') message = t('connectionPath.noPath') .replace('{source}', result.source) .replace('{target}', result.target); const nodeChip = (id: string, isEndpoint: boolean) => ( {id} ); return (
{intro} {message ? (

{message}

) : (

{t('connectionPath.resultHeading')}

{t('connectionPath.pathSummary').replace('{length}', String(result.length))}
  1. {nodeChip(result.source, true)}
  2. {result.hops.map((hop, i) => (
  3. {hop.forward ? `${hop.predicate} →` : `← ${hop.predicate}`}
  4. {nodeChip(hop.to, i === result.hops.length - 1)}
  5. ))}
)}
); }; export default ConnectionPathPanel;