ouroboros / source /src /Atlas.tsx
REXWind's picture
Publish the Ouroboros scientific showcase to CladeTeam
ba62208 verified
Raw History Blame Contribute Delete
20.6 kB
import {BrandMark} from './BrandMark';
import {MotionControl} from './Motion';
import {useEffect,useMemo,useRef,useState} from 'react';
import {AtlasMap} from './AtlasMap';
import {AtlasCoverage} from './AtlasCoverage';
import {apiUrl,atlasPath} from './api';
import type {AtlasPoints,AtlasLandmark} from './AtlasMap';
import {Molecule} from './Molecule';
import type {ProteinCase,Residue} from './types';
import './atlas.css';
type AtlasRecord={uniref_id:string;sequence:string;sequence_length:number;pred_text:string;prediction_status:string;output_hit_limit:boolean;model?:string;dataset_version?:string};
type AtlasCase=AtlasRecord&AtlasLandmark&{domain_support_display:string;pdbUrl:string;mean_plddt:number;map_xy:number[];residues?:Residue[];organism?:string;source_url?:string;sequence_sha256?:string};
type Manifest={available?:boolean;status?:string;records?:number;dataset_url?:string;revision?:string;version?:string;message?:string};
const DATASET='https://huggingface.co/datasets/REXWind/Ouroboros-Atlas';
const TOTAL=51321839;
async function readJson<T>(url:string,signal:AbortSignal):Promise<T>{const r=await fetch(apiUrl(url),{signal});if(!r.ok)throw new Error('The Atlas assets could not be loaded.');return r.json();}
function canonical(value:string){const id=value.trim().toUpperCase();return 'UniRef50_'+id.replace(/^UNIREF50_/,'');}
function download(text:string,name:string,type:string){const url=URL.createObjectURL(new Blob([text],{type})),a=document.createElement('a');a.href=url;a.download=name;a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}
function Stats(){return <div className="atlas-stats"><div><strong>51.32M</strong><span>Searchable representatives</span></div><div><strong>34</strong><span>Sequence communities</span></div><div><strong>08</strong><span>Featured proteins</span></div></div>;}
function CaseCards({cases,selected,onSelect,compact=false}:{cases:AtlasCase[];selected?:string|null;onSelect:(c:AtlasCase)=>void;compact?:boolean}){return <div className={'atlas-cases '+(compact?'atlas-cases-compact':'')}>{cases.map(c=><button key={c.uniref_id} className="atlas-case" aria-pressed={selected===c.uniref_id} onClick={()=>onSelect(c)}><span className="atlas-card-top"><span className="atlas-case-number">{String(c.number).padStart(2,'0')}</span><span>{'ATLAS SHOWCASE'}</span><span>↗</span></span><img src={c.thumbnail} alt={`Predicted structure of ${c.uniref_id.replace('UniRef50_','')}`} loading="lazy" width="352" height="244"/><span className="atlas-card-title">{c.title}</span><strong>{c.uniref_id.replace('UniRef50_','')}</strong>{!compact&&<p>{c.organism}</p>}<small>{c.sequence_length} aa · pLDDT {c.mean_plddt.toFixed(1)}</small></button>)}</div>;}
function asProtein(c:AtlasCase):ProteinCase{return {id:c.uniref_id,assetId:c.uniref_id,title:c.uniref_id,cohort:'Atlas featured illustrations',rank:0,cohortSize:8,sequence:c.sequence,prompt:c.pred_text,residues:c.residues||[],plddt:c.mean_plddt,pae:0,length:c.sequence_length,pdbUrl:c.pdbUrl,fastaUrl:'',selection:'Sequence-verified AlphaFold DB structure.',structureStatus:'Predicted',experimentalStatus:'Function not experimentally validated.',provenance:c.source_url||'',modelLabel:'AlphaFold DB'};}
export function AtlasPreview(){
const host=useRef<HTMLElement>(null),[visible,setVisible]=useState(false),[points,setPoints]=useState<AtlasPoints|null>(null),[cases,setCases]=useState<AtlasCase[]>([]),[error,setError]=useState(false);
const [selected,setSelected]=useState<AtlasCase|null>(null);
useEffect(()=>{const observer=new IntersectionObserver(entries=>{if(entries.some(e=>e.isIntersecting)){setVisible(true);observer.disconnect();}},{rootMargin:'250px'});if(host.current)observer.observe(host.current);return()=>observer.disconnect();},[]);
useEffect(()=>{if(!visible)return;const ac=new AbortController();Promise.all([readJson<AtlasPoints>('/data/atlas-preview.json',ac.signal),readJson<AtlasCase[]>('/data/atlas-cases.json',ac.signal)]).then(([p,c])=>{setPoints(p);setCases(c);}).catch(()=>{if(!ac.signal.aborted)setError(true);});return()=>ac.abort();},[visible]);
const choose=(id:string)=>{const c=cases.find(c=>c.uniref_id===id);if(c)setSelected(c);else window.location.href=atlasPath()+'?id='+encodeURIComponent(id);};
return <section className="section atlas-section" id="atlas" ref={host}>
<div className="atlas-intro"><div><p className="eyebrow"><span>OUROBOROS ATLAS</span> A MAP TO EXPLORE</p><h2>A wider world<br/><em>of protein function.</em></h2></div><div className="atlas-intro-copy"><p>Curated references cover a small corner of protein sequence space. Ouroboros helps extend functional annotation across the landscape, giving more sequences a description to explore.</p><a className="text-button" href={atlasPath()}>Search the full Atlas ↗</a></div></div>
<AtlasCoverage/>
<Stats/>
<div className="atlas-preview-layout">{points?<AtlasMap data={points} featured={cases} selectedId={selected?.uniref_id} preview onSelect={choose}/>:<div className="atlas-loading" role="status">{error?'The map preview is unavailable. Open the Atlas to retry.':'Loading the protein landscape…'}</div>}
<aside className="atlas-preview-story" aria-live="polite">{selected?<><p className="eyebrow">SHOWCASE {String(selected.number).padStart(2,'0')} / 08</p><img src={selected.thumbnail} alt={`Predicted structure of ${selected.uniref_id}`} width="352" height="244"/><h3>{selected.title}</h3><span className="atlas-story-id">{selected.uniref_id.replace('UniRef50_','')} · {selected.organism}</span><p>{selected.pred_text.replace(/([.!?])(?=[A-Z])/g,'$1 ')}</p><a className="text-button" href={atlasPath()+'?id='+encodeURIComponent(selected.uniref_id)}>Explore this protein ↗</a></>:<><span className="atlas-story-orbit" aria-hidden="true">↗</span><p className="eyebrow">FROM REFERENCE TO DESCRIPTION</p><h3>A small overlap.<br/><em>A wider possibility.</em></h3><p>Switch from Swiss-Prot overlap to Ouroboros descriptions to reveal the broader sequence space. Then select a featured protein to read its generated annotation.</p><a className="text-button" href={atlasPath()}>Open the interactive Atlas ↗</a></>}</aside>
</div>
<div className="atlas-case-heading"><h3>Eight sequences. Different possibilities.</h3><p>Select a structure to discover its story</p></div>
<CaseCards cases={cases} selected={selected?.uniref_id} onSelect={setSelected} compact/>
<div className="atlas-preview-foot"><p>{points?.ids.length.toLocaleString()||'4,000'} points in this preview. Search all {TOTAL.toLocaleString()} records by ID. Generated descriptions are functional hypotheses.</p><a className="button primary" href={atlasPath()}>Explore Atlas <span>↗</span></a></div>
</section>;
}
export function AtlasPage(){
const [points,setPoints]=useState<AtlasPoints|null>(null),[cases,setCases]=useState<AtlasCase[]>([]),[manifest,setManifest]=useState<Manifest|null>(null),[assetsError,setAssetsError]=useState(false);
const [casesState,setCasesState]=useState<'loading'|'ready'|'error'>('loading');
const [query,setQuery]=useState(()=>new URLSearchParams(window.location.search).get('id')||''),[selected,setSelected]=useState<string|null>(null),[record,setRecord]=useState<AtlasRecord|null>(null),[loading,setLoading]=useState(false),[error,setError]=useState<{title:string;body:string}|null>(null),[featuredRecord,setFeaturedRecord]=useState(false),[spin,setSpin]=useState(false),[structureColor,setStructureColor]=useState('spectrum'),[copyStatus,setCopyStatus]=useState('Copy link');
const active=useRef<AbortController|null>(null),requestNumber=useRef(0),detail=useRef<HTMLDivElement>(null),mapHost=useRef<HTMLDivElement>(null);
const mapIds=useMemo(()=>new Set(points?.ids||[]),[points]);
const selectedCase=record?cases.find(c=>c.uniref_id===record.uniref_id&&c.sequence===record.sequence):undefined;
useEffect(()=>{const ac=new AbortController();readJson<AtlasPoints>('/data/atlas-map.json',ac.signal).then(setPoints).catch(()=>{if(!ac.signal.aborted)setAssetsError(true);});readJson<AtlasCase[]>('/data/atlas-cases.json',ac.signal).then(c=>{setCases(c);setCasesState('ready');}).catch(()=>{if(!ac.signal.aborted)setCasesState('error');});readJson<Manifest>('/api/atlas/manifest',ac.signal).then(setManifest).catch(()=>{if(!ac.signal.aborted)setManifest({available:false,message:'The full Atlas lookup is currently unavailable. Please try again later.'});});return()=>{ac.abort();active.current?.abort();};},[]);
function updateUrl(id:string){const url=new URL(window.location.href);url.searchParams.set('id',id);window.history.pushState(null,'',url);}
async function lookup(raw:string,changeUrl=true){
active.current?.abort();const number=++requestNumber.current,ac=new AbortController();active.current=ac;
const id=canonical(raw);const archived=cases.find(c=>c.uniref_id===id);if(archived){openFeatured(archived,changeUrl);return;}setQuery(raw.trim());setRecord(null);setSelected(null);setError(null);setFeaturedRecord(false);setSpin(false);setCopyStatus('Copy link');
if(!/^UniRef50_[A-Z0-9]+(?:-[0-9]+)?$/.test(id)){setLoading(false);setError({title:'Enter a record ID',body:'Use a UniRef50 ID or its accession suffix, for example UniRef50_A0A9J7N2Q8 or A0A9J7N2Q8. This field does not search protein sequence fragments.'});return;}
setLoading(true);if(changeUrl)updateUrl(id);
const timeout=setTimeout(()=>ac.abort('timeout'),45000);
try{
const r=await fetch(apiUrl('/api/atlas/proteins/'+encodeURIComponent(id)),{signal:ac.signal});
if(number!==requestNumber.current)return;
if(!r.ok){if(r.status===404)setError({title:'Record not found',body:'This ID is not in the published set of 51,321,839 annotated representatives. Other cluster members and deferred long sequences are outside this release.'});else if(r.status===429)setError({title:'Lookup is busy',body:'The data service is rate limited. Please wait briefly and try again.'});else if(r.status===504)setError({title:'Lookup timed out',body:'The data service took too long to respond. The ID may still be present; please retry.'});else if(r.status===422||r.status===400)setError({title:'Check the record ID',body:'Enter a UniRef50 ID or the accession suffix of an Atlas record.'});else setError({title:'Lookup temporarily unavailable',body:'The data service could not return this record. This does not mean that the ID is absent. Please retry.'});return;}
const result:AtlasRecord=await r.json();if(number!==requestNumber.current)return;
setRecord(result);setSelected(result.uniref_id);setQuery(result.uniref_id);if(window.innerWidth<=780)detail.current?.scrollIntoView({behavior:window.matchMedia('(prefers-reduced-motion: reduce)').matches?'instant':'smooth',block:'start'});
}catch{if(number===requestNumber.current){if(ac.signal.reason==='timeout')setError({title:'Lookup timed out',body:'The data service took too long to respond. The ID may still be present; please retry.'});else if(!ac.signal.aborted)setError({title:'Could not reach the data service',body:'Check your connection and try again. No conclusion about this ID can be drawn from a connection error.'});}}
finally{clearTimeout(timeout);if(number===requestNumber.current)setLoading(false);}
}
function openFeatured(c:AtlasCase,changeUrl=true){active.current?.abort();requestNumber.current++;setLoading(false);setError(null);setRecord(c);setSelected(c.uniref_id);setQuery(c.uniref_id);setFeaturedRecord(true);setSpin(false);setCopyStatus('Copy link');if(changeUrl)updateUrl(c.uniref_id);if(window.innerWidth<=780)detail.current?.scrollIntoView({behavior:window.matchMedia('(prefers-reduced-motion: reduce)').matches?'instant':'smooth',block:'start'});}
useEffect(()=>{document.title='Ouroboros Atlas — Explore protein descriptions';if(casesState==='loading')return;const navigate=()=>{const id=new URLSearchParams(window.location.search).get('id');if(id)void lookup(id,false);else{active.current?.abort();requestNumber.current++;setLoading(false);setRecord(null);setSelected(null);setQuery('');setError(null);}};navigate();window.addEventListener('popstate',navigate);return()=>window.removeEventListener('popstate',navigate);},[casesState]);
const selectCase=(c:AtlasCase)=>{openFeatured(c);if(window.innerWidth>780)mapHost.current?.scrollIntoView({behavior:window.matchMedia('(prefers-reduced-motion: reduce)').matches?'instant':'smooth',block:'start'});};
const caseIndex=cases.findIndex(c=>c.uniref_id===selected);
const unavailable=manifest?.available===false||manifest?.status==='unavailable';
return <main className="atlas-page" id="main"><header className="atlas-page-header"><a href="/" aria-label="Ouroboros home"><BrandMark/> ouroboros</a><a href="/#atlas">← Back to the research showcase</a><MotionControl/></header><div className="atlas-page-heading"><div><p className="eyebrow">OUROBOROS ATLAS · FIX LARGE</p><h1>The protein landscape.<br/><em>Yours to explore.</em></h1><p>Explore 34 sequence communities and eight featured proteins. Search any of 51.32 million annotated representatives by ID.</p></div><Stats/></div>
{unavailable&&<p className="atlas-availability" role="status">Full-dataset lookup is currently unavailable. The map and eight featured proteins remain browsable. Lookup errors do not mean an ID is absent.</p>}
<form className="atlas-id-search" onSubmit={e=>{e.preventDefault();void lookup(query);}}><label><span>Find an Atlas record by ID</span><input name="protein-id" value={query} onChange={e=>setQuery(e.target.value)} placeholder="UniRef50_A0A9J7N2Q8 or A0A9J7N2Q8" autoComplete="off" spellCheck={false} maxLength={100}/></label><button className="button primary" type="submit" disabled={!query.trim()||loading}>{loading?'Searching…':'Find protein'} <span>→</span></button></form><p className="atlas-id-search-note">Exact ID lookup across {TOTAL.toLocaleString()} annotated representatives. Accession suffixes refer to existing Atlas records; this is not a cluster-member or sequence-fragment search.</p>
<div className="atlas-workspace" ref={mapHost}><div className="atlas-map-column">{points?<AtlasMap data={points} selectedId={selected} onSelect={id=>{void lookup(id);}} featured={cases}/>:<div className="atlas-loading" role="status">{assetsError?'The map could not be loaded. ID search is independent of the map; refresh to retry the visualization.':'Loading the protein landscape…'}</div>}</div><div ref={detail} className={'atlas-detail '+(error?'atlas-error':'')} aria-live="polite" aria-busy={loading}>
{loading?<div className="atlas-record-empty"><p className="micro">LOOKING UP THE ATLAS</p><h2>Opening your protein…</h2><p>The matching data shard is loaded on demand. The first lookup can take longer.</p></div>:error?<div className="atlas-record-empty" role="status"><h2>{error.title}</h2><p>{error.body}</p></div>:record?<>{caseIndex>=0&&<div className="atlas-case-navigation"><button aria-label="Previous showcase" onClick={()=>openFeatured(cases[(caseIndex+cases.length-1)%cases.length])}>←</button><span>SHOWCASE {String(caseIndex+1).padStart(2,'0')} / 08</span><button aria-label="Next showcase" onClick={()=>openFeatured(cases[(caseIndex+1)%cases.length])}>→</button></div>}<div className="atlas-detail-body"><p className="micro">{featuredRecord?'FEATURED PROTEIN · ARCHIVED DESCRIPTION':'ATLAS RECORD · OUROBOROS FIX LARGE'}</p><h2>{record.uniref_id}</h2><p className="atlas-detail-subtitle">{record.sequence_length} amino acids{selectedCase?.organism?' · '+selectedCase.organism:''}</p><div className="atlas-description"><span className="micro">MODEL-GENERATED DESCRIPTION</span><p>{record.pred_text?.replace(/([.!?])(?=[A-Z])/g,'$1 ')||'No readable description is available for this record.'}</p></div>{selectedCase?.domain_support_display&&<p className="atlas-domain">{selectedCase.domain_support_display.replace('Domain evidence: ','')}</p>}{record.output_hit_limit&&<p className="atlas-status">Generation reached the output-length limit. The archived description may be incomplete.</p>}{record.prediction_status&&record.prediction_status!=='ok'&&<p className="atlas-status">Prediction status: {record.prediction_status.replaceAll('_',' ')}. The original record is preserved.</p>}<details className="atlas-detail-sequence"><summary>Amino-acid sequence · {record.sequence_length} residues</summary><pre>{record.sequence}</pre></details><div className="atlas-detail-links"><button onClick={()=>download(`>${record.uniref_id}\n${record.sequence}\n`,record.uniref_id+'.fasta','text/plain')}>↓ FASTA</button><button onClick={()=>download(JSON.stringify(record,null,2)+'\n',record.uniref_id+'.json','application/json')}>↓ Record JSON</button><a href={'https://www.uniprot.org/uniref/'+encodeURIComponent(record.uniref_id)} target="_blank" rel="noreferrer">UniRef entry ↗</a><button onClick={()=>{const url=new URL(atlasPath(),window.location.origin);url.searchParams.set('id',record.uniref_id);if(navigator.clipboard)void navigator.clipboard.writeText(url.toString()).then(()=>setCopyStatus('Link copied')).catch(()=>setCopyStatus('Use the address bar to copy'));else setCopyStatus('Use the address bar to copy');}}>{copyStatus}</button></div>{points&&<p className="atlas-location">{mapIds.has(record.uniref_id)?'Highlighted in the display sample.':'This record is outside the current map sample. Its description is part of the full Atlas.'}</p>}</div>{selectedCase?<div className="atlas-structure"><div className="atlas-structure-head"><div><span className="micro">ALPHAFOLD DB · PREDICTED STRUCTURE</span><p>Mean pLDDT {selectedCase.mean_plddt.toFixed(2)}</p></div><button onClick={()=>setSpin(s=>!s)} aria-pressed={spin}>{spin?'Ⅱ Pause':'↻ Rotate'}</button></div><div className="atlas-structure-colors" role="group" aria-label="Structure coloring"><button aria-pressed={structureColor==='spectrum'} onClick={()=>setStructureColor('spectrum')}>N → C terminus</button><button aria-pressed={structureColor==='confidence'} onClick={()=>setStructureColor('confidence')}>pLDDT confidence</button></div><Molecule protein={asProtein(selectedCase)} spinning={spin} color={structureColor} fitZoom={1}/><p className="atlas-structure-note">Structure sequence matches this Atlas sequence. pLDDT measures structural confidence, not functional accuracy. This illustration has not undergone functional validation.</p><div className="atlas-case-download"><a href={selectedCase.pdbUrl} download>↓ PDB</a>{selectedCase.source_url&&<a href={selectedCase.source_url} target="_blank" rel="noreferrer">Structure source ↗</a>}</div></div>:<div className="atlas-structure"><p className="atlas-structure-note">{casesState==='loading'?'Loading structure availability…':casesState==='error'?'Featured structure metadata is unavailable. Refresh to retry.':'No sequence-verified structure is bundled for this record.'}</p></div>}</>:<div className="atlas-record-empty"><p className="micro">YOUR NEXT PROTEIN</p><h2>A point on the map.<br/>A sequence to explore.</h2><p>Click a point, choose one of the eight featured proteins below, or enter an Atlas ID. The full database extends beyond this map sample.</p><div className="atlas-empty-cases">{cases.slice(0,4).map(c=><button key={c.uniref_id} onClick={()=>openFeatured(c)}><img src={c.thumbnail} width="352" height="244" alt=""/><span>{String(c.number).padStart(2,'0')} · {c.title}</span></button>)}</div></div>}
</div></div>
<div className="atlas-case-heading"><div><p className="eyebrow">FEATURED PROTEINS</p><h2>Eight windows into protein space.</h2></div><p>Sequence-verified structures · Generated descriptions</p></div>{casesState==='error'&&<p className="atlas-status" role="status">The featured proteins could not be loaded. Refresh to retry; ID lookup remains available.</p>}<CaseCards cases={cases} selected={selected} onSelect={selectCase}/>
<AtlasCoverage/>
<p className="atlas-evidence-note">A community-stratified display sample of {points?.ids.length.toLocaleString()||'12,000'} sequences retains the archived coordinates, community colors and all eight cases. Full-corpus search covers 51,321,839 representatives of 1–511 aa. Reference overlap denotes exact sequence identity to Swiss-Prot 2024_01; it does not establish functional accuracy or novelty. Structures are predictions, and cases are selected illustrations rather than an accuracy sample. Atlas descriptions use Ouroboros FIX large; the live playground uses original large-v5. <a className="text-button" href={manifest?.dataset_url||DATASET} target="_blank" rel="noreferrer">Full dataset ↗</a> · <a className="text-button" href="/data/atlas-provenance.json">Map provenance ↗</a></p>
</main>;
}