HTML 77.2%
TypeScript 10.5%
Python 9.6%
JavaScript 2.5%
1import { Evidence } from '@/components/evidence/evidence';2import { cn } from '@/lib/cn';3import { fmtAgo } from '@/lib/format';4import type { ProvenanceEntry } from '@/lib/types';5import { TierBadge } from './badges';67function domain(url: string | null | undefined): string | null {8 if (!url) return null;9 try {10 return new URL(url).hostname.replace(/^www\./, '');11 } catch {12 return null;13 }14}1516/**17 * One-line provenance: "Source: docs.claude.com · T1 · observed 3 h ago · high". Server-safe.18 * With `slug` + `property` it appends an "Evidence" trigger that opens the drawer (`components/evidence`).19 */20export function ProvenanceInline({21 p,22 className,23 showConfidence = true,24 slug,25 property,26 value,27 entity,28}: {29 p: ProvenanceEntry;30 className?: string;31 showConfidence?: boolean;32 slug?: string;33 property?: string;34 value?: unknown;35 entity?: { name?: string; entity_type?: string } | null;36}) {37 const name = p.source_name ?? domain(p.url) ?? 'source';38 return (39 <p className={cn('flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-[11px] leading-4 text-ink-3', className)}>40 <span>Source:</span>41 {p.url ? (42 <a href={p.url} target="_blank" rel="noopener noreferrer" className="truncate text-ink-2 hover:text-accent hover:underline">43 {name}44 </a>45 ) : (46 <span className="text-ink-2">{name}</span>47 )}48 <span aria-hidden>·</span>49 <TierBadge tier={p.tier} />50 <span aria-hidden>·</span>51 <span title={p.observed_at}>observed {fmtAgo(p.observed_at)}</span>52 {showConfidence && p.confidence && (53 <>54 <span aria-hidden>·</span>55 <span className={cn(p.confidence === 'conflicted' && 'text-danger', p.confidence === 'low' && 'text-warning')}>{p.confidence}</span>56 </>57 )}58 {p.extractor && p.extractor.startsWith('llm') && (59 <>60 <span aria-hidden>·</span>61 <span title="Extracted by the local LLM factory">LLM-extracted</span>62 </>63 )}64 {slug && property && (65 <>66 <span aria-hidden>·</span>67 <Evidence slug={slug} property={property} value={value} unit={p.unit ?? null} fallback={p} entity={entity ?? null} className="text-accent">68 Evidence69 </Evidence>70 </>71 )}72 </p>73 );74}7576/** Compact source cell for tables: domain link + tier. */77export function SourceCell({ url, tier, name, observedAt, className }: { url: string | null | undefined; tier?: number | null; name?: string | null; observedAt?: string | null; className?: string }) {78 const label = name ?? domain(url);79 return (80 <span className={cn('inline-flex flex-wrap items-center gap-1.5 text-xs text-ink-3', className)}>81 {url && label ? (82 <a href={url} target="_blank" rel="noopener noreferrer" className="max-w-[16rem] truncate text-ink-2 hover:text-accent hover:underline">83 {label}84 </a>85 ) : label ? (86 <span>{label}</span>87 ) : (88 <span>—</span>89 )}90 {tier !== undefined && <TierBadge tier={tier} />}91 {observedAt && <span title={observedAt}>{fmtAgo(observedAt)}</span>}92 </span>93 );94}95