← Files Biological Sequence & Alignment ViewerARCHIVED FILE
src/sequence/pinned-inspector.tsx
10.5 KB · Sep 30, 2026 · 23:01 UTC
import { WorkbenchDisclosure } from "../ui/workbench-disclosure";
import { getLocalFeatureSegments } from "./feature-location";
import { getSelectedFeatureTranslation } from "./translation-track";
import { translateSixFrames } from "./translation";
import type {
SequenceFeature,
SequenceRecord,
SequenceSelection,
} from "./types";
import {
extractSelectedSequence,
getSelectionLength,
getSelectionSegments,
summarizeSelectedQuality,
selectionOverlapsFeature,
} from "./selection";
export function PinnedInspector({
feature,
hoverCoordinate,
onClearSelection,
record,
selection,
}: {
feature?: SequenceFeature;
hoverCoordinate?: number;
onClearSelection: () => void;
record: SequenceRecord;
selection?: SequenceSelection;
}): React.ReactElement {
const selectionLength = selection == null ? 0 : getSelectionLength(selection);
const selectedSequence =
selection == null
? null
: extractSelectedSequence(record, selection, 3_000);
const overlappingFeatures =
selection == null
? []
: record.features.filter((candidate) =>
selectionOverlapsFeature(selection, candidate),
);
const translatedSelections = overlappingFeatures.flatMap(
(overlappingFeature) => {
return selection == null
? []
: getSelectionSegments(selection).flatMap((segment) => {
const translatedSelection = getSelectedFeatureTranslation({
feature: overlappingFeature,
selectionEnd: segment.end,
selectionStart: segment.start,
});
return translatedSelection == null
? []
: [
{
feature: overlappingFeature,
segmentKey: `${segment.start}-${segment.end}`,
...translatedSelection,
},
];
});
},
);
const hoveredResidue =
hoverCoordinate == null
? null
: (record.sequence[hoverCoordinate - 1] ?? null);
const hoveredQuality =
hoverCoordinate == null
? null
: (record.quality?.phred[hoverCoordinate - 1] ?? null);
const selectedQuality =
selection == null || record.quality == null
? null
: summarizeSelectedQuality(record, selection);
const selectedTranslations =
selectedSequence == null ||
record.molecule === "protein" ||
record.molecule === "unknown"
? []
: translateSixFrames(selectedSequence).map((translation) => ({
...translation,
aminoAcids:
translation.aminoAcids.length <= 180
? translation.aminoAcids
: `${translation.aminoAcids.slice(0, 180)}…`,
}));
return (
<section
aria-label="Sequence inspector"
className="bio-sequence-inspector min-w-0"
>
<div className="space-y-4 text-sm leading-relaxed text-token-text-primary">
<section>
<div className="font-semibold">{record.sourceLabel}</div>
<div className="text-token-text-secondary">
{record.length.toLocaleString()} residues · {record.molecule}
</div>
</section>
{hoverCoordinate == null || hoveredResidue == null ? null : (
<section className="border-t border-token-border pt-3">
<div className="text-xs font-medium text-token-text-secondary">
Hover / focus
</div>
<div className="mt-1 font-mono text-token-text-primary">
{hoverCoordinate.toLocaleString()} · {hoveredResidue}
{hoveredQuality == null ? "" : ` · Q${hoveredQuality}`}
</div>
</section>
)}
{selection == null ? (
<p className="text-token-text-secondary">
Drag across the sequence to pin a coordinate range.
</p>
) : (
<section className="border-t border-token-border pt-3">
<div className="flex items-start justify-between gap-2">
<div>
<div className="font-semibold">
{getSelectionSegments(selection)
.map(
({ end, start }) =>
`${start.toLocaleString()}–${end.toLocaleString()}`,
)
.join(" + ")}
</div>
<div className="mt-1 text-xs text-token-text-secondary">
{selectionLength.toLocaleString()} residues
</div>
</div>
<button
className="hover:bg-token-main-surface-secondary rounded border border-token-border px-2 py-0.5 text-xs font-medium text-token-text-secondary"
onClick={onClearSelection}
title="Clear selection (Esc)"
type="button"
>
Clear
</button>
</div>
<code className="mt-2 block max-h-32 overflow-auto rounded-md bg-token-main-surface-secondary p-2 font-mono break-all text-token-text-primary">
{selectedSequence}
</code>
{selectionLength > (selectedSequence?.length ?? 0) ? (
<div className="mt-1 text-xs text-token-text-tertiary">
Showing the first{" "}
{(selectedSequence?.length ?? 0).toLocaleString()} residues of
this selection.
</div>
) : null}
{selectedQuality == null ? null : (
<div className="mt-2 text-xs text-token-text-secondary">
FASTQ quality Q{selectedQuality.min}–Q{selectedQuality.max} ·
mean Q{selectedQuality.mean.toFixed(1)} across selected residues
</div>
)}
{selectedTranslations.length === 0 ? null : (
<WorkbenchDisclosure
className="mt-3 space-y-1"
id="sequence.inspector-six-frame"
label="Six-frame translation"
summaryClassName="cursor-pointer text-sm font-medium text-token-text-primary"
>
{selectedTranslations.map(({ aminoAcids, frame }) => (
<div
className="grid grid-cols-[2rem_1fr] gap-2 text-xs text-token-text-secondary"
key={frame}
>
<span>{frame > 0 ? `+${frame}` : frame}</span>
<code className="font-mono break-all">
{aminoAcids || "—"}
</code>
</div>
))}
</WorkbenchDisclosure>
)}
{overlappingFeatures.length === 0 ? null : (
<div className="mt-3 space-y-1">
<div className="text-xs font-semibold tracking-wide text-token-text-tertiary uppercase">
Overlapping features
</div>
{overlappingFeatures.map((overlappingFeature) => (
<div
className="text-xs text-token-text-secondary"
key={overlappingFeature.id}
>
{overlappingFeature.label ?? overlappingFeature.type} ·{" "}
{overlappingFeature.start}–{overlappingFeature.end}
</div>
))}
</div>
)}
{translatedSelections.length === 0 ? null : (
<div className="mt-3 space-y-1">
<div className="text-xs font-semibold tracking-wide text-token-text-tertiary uppercase">
CDS translation overlap
</div>
{translatedSelections.map(
({
aminoAcidEnd,
aminoAcidStart,
feature: translatedFeature,
segmentKey,
translation,
}) => (
<div
className="text-xs text-token-text-secondary"
key={`${translatedFeature.id}:${segmentKey}`}
>
{translatedFeature.label ?? translatedFeature.type} · aa{" "}
{aminoAcidStart}–{aminoAcidEnd} ·{" "}
<code className="font-mono">{translation}</code>
</div>
),
)}
</div>
)}
</section>
)}
{feature == null ? null : (
<section className="border-t border-token-border pt-3">
<div className="font-semibold">{feature.label ?? feature.type}</div>
<div className="text-token-text-secondary">
{feature.type} · {feature.start}–{feature.end} · {feature.strand}
</div>
{feature.sourceLocation == null ? null : (
<div className="mt-1 font-mono text-xs break-all text-token-text-tertiary">
{feature.sourceLocation}
</div>
)}
{getLocalFeatureSegments(feature).length <= 1 ? null : (
<div className="mt-1 text-xs text-token-text-secondary">
Segments:{" "}
{getLocalFeatureSegments(feature)
.map(({ end, start }) => `${start}–${end}`)
.join(", ")}
</div>
)}
{feature.translation == null ? null : (
<>
<code className="mt-2 block max-h-40 overflow-auto rounded-md bg-token-main-surface-secondary p-2 font-mono break-all text-token-text-primary">
{feature.translation}
</code>
{feature.translationTrackReliable === false ? (
<p className="mt-2 text-xs text-token-text-secondary">
Codon-aligned track withheld because the source location is
compound or ambiguous; the source-provided translation
remains inspectable here.
</p>
) : null}
</>
)}
{Object.keys(feature.qualifiers).length === 0 ? null : (
<dl className="mt-3 grid gap-1 text-xs">
{Object.entries(feature.qualifiers).map(([key, value]) => (
<div className="grid grid-cols-[5rem_1fr] gap-2" key={key}>
<dt className="text-token-text-tertiary">{key}</dt>
<dd className="break-words text-token-text-secondary">
{Array.isArray(value) ? value.join("; ") : value}
</dd>
</div>
))}
</dl>
)}
</section>
)}
</div>
</section>
);
}
SHA-256: f0dc91e677051baf53618a8590dd0dd02bc5ca5efd2295e6976996e8cf64316c