Spaces:
Running
Running
File size: 3,785 Bytes
ace64e6 c83d9b9 ec313db 773cf91 ace64e6 ec313db c83d9b9 ace64e6 c83d9b9 ec313db ace64e6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | 'use client';
import { useMemo } from 'react';
import { Download } from '@phosphor-icons/react';
import type { PairwiseAlignResult } from '@/types/pipeline';
import PhyloTreeViewer from '@/components/phylo/PhyloTreeViewer';
import { ConservationTrack } from '@/components/alignment/ConservationTrack';
import { PairwiseResultDisplay } from '@/components/alignment/PairwiseResultDisplay';
import { AlignmentStatsBar } from '@/components/alignment/AlignmentStatsBar';
import { AlignmentBlock } from '@/components/alignment/AlignmentBlock';
import { computeAlignmentStats, parseAlignedFasta } from '@/lib/alignment-stats';
interface MSAResultPanelProps {
alnFasta: string;
phylotree?: string | null;
sequenceCount?: number;
alignmentMode?: 'global' | 'local';
pairwise?: PairwiseAlignResult | null;
pairwiseSubject?: string | null;
jobId?: string;
}
export function MSAResultPanel({
alnFasta,
phylotree,
sequenceCount,
alignmentMode,
pairwise,
pairwiseSubject,
jobId,
}: MSAResultPanelProps) {
const { seqs } = useMemo(() => parseAlignedFasta(alnFasta), [alnFasta]);
const stats = useMemo(() => computeAlignmentStats(seqs), [seqs]);
const download = () => {
const blob = new Blob([alnFasta], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `msa-${jobId?.slice(0, 8) ?? 'result'}.fasta`;
a.click();
URL.revokeObjectURL(url);
};
return (
<div className="space-y-4">
<div className="data-card p-5">
<div className="flex items-center justify-between mb-3">
<div>
<h3 className="text-sm font-semibold text-text-primary">Multiple Sequence Alignment</h3>
<p className="text-xs text-text-muted mt-0.5">
{sequenceCount ?? seqs.length} sequences aligned via Clustal Omega
{alignmentMode === 'local' && (
<span className="ml-2 px-2 py-0.5 rounded-full bg-accent-amber/10 border border-accent-amber/30 text-accent-amber font-medium">
local refinement
</span>
)}
</p>
</div>
<button
onClick={download}
className="px-3 py-1.5 rounded-lg border border-glass-border text-xs text-text-secondary hover:bg-surface-1 hover:border-accent-cyan/40 transition"
>
<Download className="w-3.5 h-3.5 inline mr-1" />FASTA
</button>
</div>
<AlignmentStatsBar stats={stats} className="mb-3" />
<AlignmentBlock alnFasta={alnFasta} />
</div>
{seqs.length >= 2 && (
<div className="data-card p-5">
<ConservationTrack alignedSeqs={seqs} />
</div>
)}
{pairwise && (
<div className="data-card p-5">
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-semibold text-text-primary">
Query vs Top Hit — {pairwise.mode === 'local' ? 'Smith-Waterman (local)' : 'Needleman-Wunsch (global)'}
</h3>
<span className="text-[10px] px-2 py-0.5 rounded-full bg-accent-amber/10 border border-accent-amber/30 text-accent-amber font-medium">
{pairwise.mode} refinement
</span>
</div>
<PairwiseResultDisplay
result={pairwise}
queryLabel="Query"
subjectLabel={pairwiseSubject || 'Top hit'}
/>
</div>
)}
{phylotree && (
<div className="data-card p-5">
<h3 className="text-sm font-semibold text-text-primary mb-2">Phylogenetic Tree</h3>
<PhyloTreeViewer newick={phylotree} alignment={alnFasta} />
</div>
)}
</div>
);
}
|