← Files Biological Sequence & Alignment ViewerARCHIVED FILE
src/msa/metric-tracks.tsx
18 KB · Oct 3, 2026 · 06:20 UTC
import clsx from "clsx";
import type { WheelEventHandler } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import type { MsaDerivedAnalysis } from "./analysis";
import type { MsaColumnSummary } from "./conservation";
import type { MsaDocument, MsaMetricTrackKey, MsaMoleculeType } from "./types";
import {
MSA_ROW_LABEL_WIDTH_PX,
type MsaViewportSlice,
} from "./virtualization";
const SEQUENCE_LOGO_HEIGHT_PX = 48;
const MIN_VISIBLE_LOGO_LABEL_HEIGHT_PX = 8;
type TrackKey = MsaMetricTrackKey;
export function getAvailableMsaMetricTracks({
moleculeType,
referenceAvailable,
rnaStructureAvailable,
}: {
moleculeType: MsaMoleculeType;
referenceAvailable: boolean;
rnaStructureAvailable: boolean;
}): Array<MsaMetricTrackKey> {
const supportsConservation =
moleculeType === "protein" ||
moleculeType === "dna" ||
moleculeType === "rna" ||
moleculeType === "nucleic-acid-ambiguous";
return [
"identity",
"gap",
...(supportsConservation ? ["modality-conservation" as const] : []),
...(referenceAvailable ? ["mismatch" as const] : []),
...(rnaStructureAvailable ? ["rna-structure" as const] : []),
"sequence-logo",
];
}
export function getDefaultMsaMetricTracks(
document: MsaDocument,
): Array<MsaMetricTrackKey> {
return getAvailableMsaMetricTracks({
moleculeType: document.displayInterpretation.moleculeType,
referenceAvailable: false,
rnaStructureAvailable: document.rnaStructure != null,
}).filter((track) => track !== "sequence-logo");
}
export function MsaMetricTrackControls({
document,
enabledTracks,
onEnabledTracksChange,
onSequenceLogoHelpChange,
referenceSequence,
showSequenceLogoHelp,
}: {
document: MsaDocument;
enabledTracks: Array<MsaMetricTrackKey>;
onEnabledTracksChange: (tracks: Array<MsaMetricTrackKey>) => void;
onSequenceLogoHelpChange: (open: boolean) => void;
referenceSequence: string | null;
showSequenceLogoHelp: boolean;
}): React.ReactElement {
const intl = useIntl();
const availableTracks = getAvailableMsaMetricTracks({
moleculeType: document.displayInterpretation.moleculeType,
referenceAvailable: referenceSequence != null,
rnaStructureAvailable: document.rnaStructure != null,
});
const enabledTrackSet = new Set(enabledTracks);
return (
<section
aria-label="Quantitative track display"
className="mt-4 space-y-3 border-t border-token-border pt-3 text-xs text-token-text-secondary"
>
<h3 className="font-medium text-token-text-primary">
Quantitative tracks
</h3>
<div className="grid gap-2">
{availableTracks.map((track) => (
<label className="flex items-center gap-1" key={track}>
<input
checked={enabledTrackSet.has(track)}
onChange={(event) =>
onEnabledTracksChange(
event.target.checked
? [...enabledTracks, track]
: enabledTracks.filter((value) => value !== track),
)
}
type="checkbox"
/>
{formatTrackLabel(intl, track, document)}
{track === "sequence-logo" ? (
<span className="rounded border border-token-border px-1 text-[10px] text-token-text-tertiary">
<FormattedMessage
id="codex.filePreview.msa.track.sequenceLogoOptional"
defaultMessage="optional"
description="Small badge marking the compact sequence-logo track as an optional advanced view."
/>
</span>
) : null}
</label>
))}
</div>
{enabledTrackSet.has("sequence-logo") ? (
<SequenceLogoHelp
onOpenChange={onSequenceLogoHelpChange}
open={showSequenceLogoHelp}
/>
) : null}
</section>
);
}
export function MsaMetricTracks({
analysis,
cellWidth,
columns,
document,
enabledTracks,
onHorizontalWheel,
referenceSequence,
slice,
}: {
analysis: MsaDerivedAnalysis | null;
cellWidth: number;
columns: Array<number>;
document: MsaDocument;
enabledTracks: Array<MsaMetricTrackKey>;
onHorizontalWheel: WheelEventHandler<HTMLElement>;
referenceSequence: string | null;
slice: MsaViewportSlice;
}): React.ReactElement {
const intl = useIntl();
const availableTracks = getAvailableMsaMetricTracks({
moleculeType: document.displayInterpretation.moleculeType,
referenceAvailable: referenceSequence != null,
rnaStructureAvailable: document.rnaStructure != null,
});
const enabledTrackSet = new Set(enabledTracks);
const visibleTracks = availableTracks.filter((track) =>
enabledTrackSet.has(track),
);
return (
<section
aria-label={intl.formatMessage({
id: "codex.filePreview.msa.metricTracks",
defaultMessage: "MSA metric tracks",
description:
"Accessible label for the quantitative histogram track stack in the MSA viewer.",
})}
className="overflow-hidden border-b border-token-border bg-token-main-surface-primary px-3 py-2 text-xs text-token-text-secondary"
onWheel={onHorizontalWheel}
>
<div className="mb-2 flex flex-wrap items-center gap-2">
<span className="font-medium text-token-text-primary">
<FormattedMessage
id="codex.filePreview.msa.metricTracks"
defaultMessage="Tracks"
description="Heading for the MSA quantitative track stack."
/>
</span>
</div>
<div className="mb-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[11px] text-token-text-tertiary">
<span>
<FormattedMessage
id="codex.filePreview.msa.metricTracksVisibleColumns"
defaultMessage="Tracks show the currently displayed alignment columns {start, number}-{end, number}."
description="Helper text explaining which MSA alignment columns are represented in the quantitative track panel."
values={{
end: slice.visibleColumnEnd,
start: slice.visibleColumnStart + 1,
}}
/>
</span>
</div>
{analysis == null ? (
<div className="bg-token-main-surface-secondary rounded-md px-2 py-1 text-[11px] text-token-text-tertiary">
<FormattedMessage
id="codex.filePreview.msa.metricTracksPending"
defaultMessage="Computing quantitative column tracks…"
description="Placeholder shown while MSA quantitative tracks are still being computed."
/>
</div>
) : visibleTracks.length === 0 ? (
<div className="bg-token-main-surface-secondary rounded-md px-2 py-1 text-[11px] text-token-text-tertiary">
<FormattedMessage
id="codex.filePreview.msa.metricTracksHidden"
defaultMessage="All analytical tracks are currently hidden."
description="Placeholder shown when every MSA quantitative track has been disabled."
/>
</div>
) : (
<div className="space-y-1">
<TrackColumnRuler cellWidth={cellWidth} columns={columns} />
{visibleTracks.map((track) => (
<MetricTrackRow
analysis={analysis}
cellWidth={cellWidth}
columns={columns}
document={document}
key={track}
track={track}
/>
))}
</div>
)}
</section>
);
}
function MetricTrackRow({
analysis,
cellWidth,
columns,
document,
track,
}: {
analysis: MsaDerivedAnalysis;
cellWidth: number;
columns: Array<number>;
document: MsaDocument;
track: TrackKey;
}): React.ReactElement {
const intl = useIntl();
const isLogo = track === "sequence-logo";
return (
<div className="flex items-stretch">
<div
className="sticky left-0 z-10 shrink-0 bg-token-main-surface-primary pr-2 text-right text-[11px] text-token-text-secondary"
style={{ width: MSA_ROW_LABEL_WIDTH_PX }}
>
{formatTrackLabel(intl, track, document)}
</div>
{columns.map((column) => (
<TrackCell
analysis={analysis}
column={column}
document={document}
isLogo={isLogo}
key={`${track}:${column}`}
style={{ width: cellWidth }}
track={track}
/>
))}
</div>
);
}
function TrackColumnRuler({
cellWidth,
columns,
}: {
cellWidth: number;
columns: Array<number>;
}): React.ReactElement {
const intl = useIntl();
return (
<div className="flex items-stretch">
<div
className="sticky left-0 z-10 shrink-0 bg-token-main-surface-primary pr-2 text-right text-[11px] text-token-text-secondary"
style={{ width: MSA_ROW_LABEL_WIDTH_PX }}
>
<FormattedMessage
id="codex.filePreview.msa.trackColumns"
defaultMessage="Alignment cols"
description="Label for the coordinate ruler shown above MSA quantitative tracks."
/>
</div>
{columns.map((column, index) => {
const shouldLabel =
index === 0 ||
index === columns.length - 1 ||
(column + 1) % 10 === 0;
const shouldMajorTick = (column + 1) % 10 === 0;
return (
<span
className="relative inline-flex h-5 shrink-0 items-start justify-center text-[9px] text-token-text-tertiary"
key={column}
style={{ width: cellWidth }}
title={intl.formatMessage(
{
id: "codex.filePreview.msa.trackColumnCoordinate",
defaultMessage: "Alignment column {column, number}",
description:
"Accessible label for one quantitative-track coordinate cell in the MSA viewer.",
},
{ column: column + 1 },
)}
>
<span
aria-hidden="true"
className={clsx(
"absolute bottom-0 left-1/2 -translate-x-1/2 border-l border-token-border",
shouldMajorTick ? "h-2" : "h-1",
)}
/>
{shouldLabel ? (
<span className="absolute top-0">{column + 1}</span>
) : null}
</span>
);
})}
</div>
);
}
function SequenceLogoHelp({
onOpenChange,
open,
}: {
onOpenChange: (open: boolean) => void;
open: boolean;
}): React.ReactElement {
return (
<details
className="rounded border border-token-border px-2 py-1"
onToggle={(event) => onOpenChange(event.currentTarget.open)}
open={open}
>
<summary className="cursor-interaction font-medium text-token-text-secondary select-none">
<FormattedMessage
id="codex.filePreview.msa.metricTracksSequenceLogoHelpSummary"
defaultMessage="What does Sequence logo show?"
description="Expandable help summary for the compact MSA sequence-logo track."
/>
</summary>
<div className="mt-1 max-w-2xl space-y-1 text-[11px] text-token-text-tertiary">
<p>
<FormattedMessage
id="codex.filePreview.msa.metricTracksSequenceLogoHelpMeaning"
defaultMessage="Each stack summarizes the residue or base mixture at one alignment column."
description="Sequence-logo help text explaining what one per-column stack represents."
/>
</p>
<p>
<FormattedMessage
id="codex.filePreview.msa.metricTracksSequenceLogoHelpHeights"
defaultMessage="Tall single-letter stacks indicate a highly informative column; mixed letters indicate compositional variation."
description="Sequence-logo help text explaining tall versus mixed logo stacks."
/>
</p>
<p>
<FormattedMessage
id="codex.filePreview.msa.metricTracksSequenceLogoHelpContribution"
defaultMessage="Total stack height reflects information content, while each letter’s height reflects that symbol’s contribution."
description="Sequence-logo help text explaining total height and letter-height semantics."
/>
</p>
</div>
</details>
);
}
function TrackCell({
analysis,
column,
document,
isLogo,
style,
track,
}: {
analysis: MsaDerivedAnalysis;
column: number;
document: MsaDocument;
isLogo: boolean;
style: React.CSSProperties;
track: TrackKey;
}): React.ReactElement {
const summary = analysis.summaries[column];
if (isLogo) {
return (
<span
aria-hidden="true"
className="bg-token-main-surface-secondary relative inline-flex h-12 shrink-0 items-end justify-center overflow-hidden border border-token-border/60"
style={style}
>
<SequenceLogoColumn document={document} summary={summary} />
</span>
);
}
const layers = getTrackLayers({
analysis,
column,
summary,
track,
});
return (
<span
aria-hidden="true"
className="bg-token-main-surface-secondary relative inline-flex h-5 shrink-0 items-end overflow-hidden border border-token-border/60"
style={style}
>
{layers.map((layer) => (
<span
className={clsx("absolute inset-x-0 bottom-0", layer.className)}
key={`${track}:${column}:${layer.className}`}
style={{
height: `${Math.max(0, Math.min(1, layer.fraction)) * 100}%`,
}}
/>
))}
</span>
);
}
function SequenceLogoColumn({
document,
summary,
}: {
document: MsaDocument;
summary: MsaColumnSummary | undefined;
}): React.ReactElement | null {
if (summary == null) {
return null;
}
const entries = Object.entries(summary.weightedSymbolFractions)
.filter(([, fraction]) => fraction > 0)
.sort((left, right) => left[1] - right[1]);
if (entries.length === 0) {
return null;
}
const maxBits =
document.displayInterpretation.moleculeType === "protein"
? Math.log2(20)
: 2;
const entropy = entries.reduce(
(total, [, fraction]) => total - fraction * Math.log2(fraction),
0,
);
const informationScale =
maxBits === 0 ? 0 : Math.max(0, maxBits - entropy) / maxBits;
return (
<span className="absolute inset-x-0 bottom-0 flex h-full flex-col-reverse">
{entries.map(([symbol, fraction]) => {
const heightFraction = fraction * informationScale;
const showSymbol =
heightFraction * SEQUENCE_LOGO_HEIGHT_PX >=
MIN_VISIBLE_LOGO_LABEL_HEIGHT_PX;
return (
<span
className="flex min-h-0 items-center justify-center overflow-hidden text-[9px] leading-none font-semibold text-token-text-primary"
key={symbol}
style={{ height: `${heightFraction * 100}%` }}
>
{showSymbol ? symbol : null}
</span>
);
})}
</span>
);
}
function getTrackLayers({
analysis,
column,
summary,
track,
}: {
analysis: MsaDerivedAnalysis;
column: number;
summary: MsaColumnSummary | undefined;
track: TrackKey;
}): Array<{ className: string; fraction: number }> {
switch (track) {
case "gap":
return [
{
className: "bg-red-400/80",
fraction: summary?.gapFraction ?? 0,
},
];
case "identity":
return [
{
className: "bg-blue-500/80",
fraction: summary?.identity ?? 0,
},
];
case "mismatch":
return [
{
className: "bg-orange-400/80",
fraction: analysis.mismatchDensityByColumn[column] ?? 0,
},
];
case "modality-conservation":
return [
{
className: "bg-emerald-500/80",
fraction: summary?.conservationNormalized ?? 0,
},
];
case "rna-structure": {
const structure = analysis.rnaStructureConsensusByColumn[String(column)];
if (structure == null) {
return [];
}
return [
{
className: "bg-red-400/65",
fraction: structure.invalidFraction,
},
{
className: "bg-purple-500/80",
fraction: structure.validPairFraction,
},
];
}
case "sequence-logo":
return [];
}
}
function formatTrackLabel(
intl: ReturnType<typeof useIntl>,
track: TrackKey,
document: MsaDocument,
): string {
switch (track) {
case "gap":
return intl.formatMessage({
id: "codex.filePreview.msa.track.gaps",
defaultMessage: "Gap occupancy",
description: "MSA quantitative track label for gap occupancy.",
});
case "identity":
return intl.formatMessage({
id: "codex.filePreview.msa.track.identity",
defaultMessage: "Identity histogram",
description: "MSA quantitative track label for per-column identity.",
});
case "mismatch":
return intl.formatMessage({
id: "codex.filePreview.msa.track.mismatches",
defaultMessage: "Mismatch density",
description: "MSA quantitative track label for mismatch density.",
});
case "modality-conservation":
return document.displayInterpretation.moleculeType === "protein"
? intl.formatMessage({
id: "codex.filePreview.msa.track.proteinConservation",
defaultMessage: "Protein conservation",
description:
"MSA quantitative track label for protein relative-entropy conservation.",
})
: intl.formatMessage({
id: "codex.filePreview.msa.track.nucleotideConservation",
defaultMessage: "Nucleotide conservation",
description:
"MSA quantitative track label for nucleotide information-content conservation.",
});
case "rna-structure":
return intl.formatMessage({
id: "codex.filePreview.msa.track.rnaStructureConsensus",
defaultMessage: "RNA structure consensus",
description:
"MSA quantitative track label for RNA paired-column structure consensus.",
});
case "sequence-logo":
return intl.formatMessage({
id: "codex.filePreview.msa.track.sequenceLogo",
defaultMessage: "Sequence logo (mix + information)",
description: "MSA quantitative track label for sequence logos.",
});
}
}
SHA-256: 1334fce3b34c1377fd17e6946869c9775d520f303f7a49382c249955922069c3