import React, { useState, useRef } from 'react'; import { THEME } from '../theme.js'; import { MediaConfBar, OxygenConfArc, IntervalBar, MonoTag, SourceBadge } from './Primitives.jsx'; import { tempColor, pHColor, saltColor } from '../theme.js'; const QUICK_TRY = [ { label: 'Thermus thermophilus', value: 'Thermus thermophilus' }, { label: 'GCF_000005845.2', value: 'GCF_000005845.2' }, { label: 'Pyrococcus furiosus', value: 'Pyrococcus furiosus' }, ]; export default function PredictBar({ result, setResult }) { const [query, setQuery] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [hits, setHits] = useState(null); const fileRef = useRef(null); function isAccession(s) { const u = s.trim().toUpperCase(); return u.startsWith('GCA_') || u.startsWith('GCF_'); } async function runPredict(target) { setBusy(true); setError(null); setHits(null); try { const r = await fetch('/api/predict', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ target, top_k: 8 }), }); if (!r.ok) { const t = await r.text(); throw new Error(t || `HTTP ${r.status}`); } const data = await r.json(); setResult(data); } catch (e) { setError(String(e.message || e)); } finally { setBusy(false); } } async function onSubmit() { const q = query.trim(); if (!q) return; if (isAccession(q)) { await runPredict(q); return; } // Treat as organism name → NCBI search → user picks setBusy(true); setError(null); try { const r = await fetch(`/api/ncbi-search?q=${encodeURIComponent(q)}`); const data = await r.json(); if (!data.hits?.length) { setError(`No NCBI Assembly hits for "${q}".`); } else if (data.hits.length === 1) { await runPredict(data.hits[0].accession); } else { setHits(data.hits); } } catch (e) { setError(String(e.message || e)); } finally { setBusy(false); } } async function onFile(file) { if (!file) return; const text = await file.text(); await runPredict(text); } return (
Predict a medium paste a name, NCBI accession, FASTA, or upload a genome
setQuery(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && onSubmit()} placeholder='e.g. "Thermus thermophilus" GCF_000008125.1 >my_mag' style={{ flex: 1, border: 'none', background: 'transparent', outline: 'none', font: `400 13px ${THEME.font}`, color: THEME.ink, height: 22 }} />
onFile(e.target.files?.[0])} />
try: {QUICK_TRY.map((q) => ( ))}
{error && (
{error}
)} {hits && (
{hits.length} NCBI matches — pick one
{hits.map((h) => ( ))}
)}
{result && setResult(null)} />}
); } function PredictResultBanner({ result, onClear }) { const p = result.phenotypes || {}; const top = result.media?.[0]; const T = p.optimal_temperature_c || {}; const pH = p.optimal_ph || {}; const O2 = p.oxygen_requirement || {}; const salt = p.salt_tolerance_pct || {}; return (
Prediction · {result.accession} · {result.n_contigs} contigs · {result.n_cds} CDS · GC {(result.gc * 100).toFixed(1)}%
Try {top?.name} {top && <>  {top.medium_id}}
{top?.recipe && (
{top.recipe}
)}
{T.prediction !== undefined && ( )} {pH.prediction !== undefined && ( )} {O2.prediction !== undefined && (
O₂
{O2.prediction}
)} {salt.prediction !== undefined && ( )}
); } function PhenoStat({ label, value, sub, color }) { return (
{label}
{value}
{sub}
); }