← Files Biological Sequence & Alignment ViewerARCHIVED FILE
src/sequence/sequence-rich-viewer.tsx
90.5 KB · Sep 30, 2026 · 23:01 UTC
import {
useCallback,
useContext,
useEffect,
useMemo,
useReducer,
useRef,
useState,
} from "react";
import { createArtifactStateKey } from "../artifact-state-key";
import { decodeBamWindowToSam } from "../bam-decoder";
import { decodeCramWindowToSam } from "../cram-decoder";
import { useModelContext, type ModelContextUpdater } from "../model-context";
import { alignSequences, exportAlignedFasta } from "../msa/alignment-editing";
import {
applySequenceDurableDocumentPatches,
createDurableSequenceState,
SequenceDurableViewerStateContext,
} from "../persistent/durable-viewer-state";
import {
MAX_SEQUENCE_SOURCE_EDIT_CHUNK_BYTES,
type ScientificSequenceDataClient,
} from "../persistent/scientific-data-client";
import {
createSequenceOriginalSourceReplacementPlan,
sequenceOriginalSourceUploadChunks,
streamSequenceOriginalSourceReplacement,
type SequenceOriginalSourceReplacementPlan,
} from "../persistent/streaming-source-replacement";
import { resolveViewerTarget } from "../target-resolution";
import { Button } from "../ui/button";
import type {
QueuedSequenceViewerCommand,
SequenceViewerCommandResult,
} from "../viewer-commands";
import type {
SequenceViewerAnnotationRequest,
SequenceViewerAnalysisRequest,
SequenceViewerEditRequest,
} from "../viewer-operations";
import type { SequenceWorkspaceArtifactPublisher } from "../views/workbench-persistence";
import type { SequenceWorkspaceSessionClient } from "../views/workspace-sessions";
import type { SequenceWorkspaceTrackBrowserClient } from "../views/workspace-tracks";
import { exportSequenceWorkbench } from "../workbench-exports";
import {
createSequenceWorkbenchState,
parseWorkbenchSession,
sequenceWorkbenchReducer,
serializeWorkbenchSession,
settleRestoredJobs,
} from "../workbench-state";
import { workspaceSessionDefaultName } from "../workspace-session-controls";
import {
ChromatogramPanel,
type ChromatogramViewState,
} from "./chromatogram-panel";
import { clampSequenceCoordinate } from "./coordinate-map";
import { EvidenceTrackPanel } from "./evidence-track-panel";
import { FastqSummaryPanel } from "./fastq-summary-panel";
import {
createFastqQualityViewState,
type FastqQualityReportState,
} from "./fastq-quality-analysis";
import { getLocalFeatureSegments } from "./feature-location";
import { resolveSequenceFeatureSelector } from "./feature-resolution";
import { getGeneticCode } from "./genetic-code";
import {
createSequenceInterfaceSettings,
validateSequenceInterfaceSettingsForSource,
} from "./interface-state";
import {
createSequenceInteractionState,
sequenceInteractionReducer,
} from "./interaction-state";
import { MetadataPanel } from "./metadata-panel";
import { createSequenceViewerModelContext } from "./model-context";
import {
displaySelectionToSource,
orientSearchHit,
orientSequenceRecord,
sourceCoordinateToDisplay,
sourceRangeToDisplay,
} from "./orientation";
import { PerformanceBanners } from "./performance-banners";
import { PinnedInspector } from "./pinned-inspector";
import { QualityTrack } from "./quality-track";
import {
DEFAULT_READ_PILEUP_STATE,
getReadPileupEntryForRecord,
type ReadPileupState,
} from "./read-pileup";
import {
DEFAULT_SEQUENCE_RECORD_BROWSER_STATE,
normalizeSequenceRecordBrowserState,
RecordListPanel,
} from "./record-list-panel";
import { SearchPanel } from "./search-panel";
import { createOriginSpanningSelection } from "./selection";
import { SequenceLegend } from "./sequence-legend";
import {
normalizeSequenceAnnotationIndexState,
SequenceOverview,
type SequenceAnnotationIndexState,
} from "./sequence-overview";
import {
getCompatibleSequencePalettes,
getDefaultSequencePalette,
} from "./sequence-palette";
import { SequenceRenderer } from "./sequence-renderer";
import { SequenceToolbar } from "./sequence-toolbar";
import { SequenceWorkbenchPanel } from "./sequence-workbench-panel";
import { parseSequenceTrack, type SequenceTrackFormat } from "./tracks";
import type {
SequenceDocument,
SequenceFeature,
SequencePaletteId,
SequenceRecord,
SequenceSearchHit,
SequenceSelection,
} from "./types";
import { useSequenceSearch } from "./use-sequence-search";
import {
createSequenceInterfaceSnapshot,
isSequenceInterfaceCommand,
useSequenceInterfaceCommands,
} from "./use-sequence-interface-commands";
import {
isWorkbenchOperationCommand,
createSequenceWorkbenchSession,
runSequenceWorkbenchJob,
useSequenceWorkbenchCommands,
type SequenceWorkbenchRestoreSource,
type SequenceWorkbenchView,
} from "./use-sequence-workbench-commands";
import { WarningsDrawer } from "./warnings-drawer";
import {
applySequenceAnnotationRequest,
applySequenceEditRequest,
} from "./workbench-controller";
export function SequenceRichViewer({
browseWorkspaceTracks,
command,
document: initialDocument,
onCommandResult,
onOpenAlignment,
publishWorkspaceArtifact,
sourceStateKeyOverride,
toolbarRevealed,
toolbarVisible = true,
updateModelContext,
viewerSessionId,
workspaceSessions,
}: {
browseWorkspaceTracks?: SequenceWorkspaceTrackBrowserClient;
command?: QueuedSequenceViewerCommand;
document: SequenceDocument;
onCommandResult?: (
command: QueuedSequenceViewerCommand,
result: SequenceViewerCommandResult,
) => void;
onOpenAlignment?: (alignedFasta: string, name: string) => void;
publishWorkspaceArtifact?: SequenceWorkspaceArtifactPublisher;
sourceStateKeyOverride?: string;
toolbarRevealed?: boolean;
toolbarVisible?: boolean;
updateModelContext?: ModelContextUpdater;
viewerSessionId?: string;
workspaceSessions?: SequenceWorkspaceSessionClient;
}): React.ReactElement {
const durableViewerState = useContext(SequenceDurableViewerStateContext);
const handledCommandIdRef = useRef<string | undefined>(undefined);
const cancelledJobsRef = useRef(new Set<string>());
const activeSourceEditRef = useRef<AbortController | null>(null);
const [sourceSaveState, setSourceSaveState] = useState<{
status: "idle" | "saving" | "saved";
message?: string;
error?: string;
}>({ status: "idle" });
const restoredCheckpointRef = useRef<string | null>(null);
const restoredSearchHitRef = useRef<number | null>(null);
const [workbenchState, dispatchWorkbench] = useReducer(
sequenceWorkbenchReducer,
initialDocument,
createSequenceWorkbenchState,
);
const workbenchStateRef = useRef(workbenchState);
workbenchStateRef.current = workbenchState;
const document = workbenchState.document;
const sourceStateKey = useMemo(
() =>
sourceStateKeyOverride ??
createArtifactStateKey(
initialDocument.records
.map(({ id, sequence }) => `${id}\u001f${sequence}`)
.join("\u001e"),
initialDocument.fileName,
),
[initialDocument, sourceStateKeyOverride],
);
const sourceStateKeyRef = useRef(sourceStateKey);
const sourceDocumentReady = sourceStateKeyRef.current === sourceStateKey;
useEffect(() => {
if (sourceStateKeyRef.current === sourceStateKey) return;
sourceStateKeyRef.current = sourceStateKey;
dispatchWorkbench({
document: initialDocument,
type: "reset-sequence-document",
});
dispatchInteraction({ type: "clear-selection" });
dispatchInteraction({
recordId: initialDocument.records[0]?.id ?? "",
type: "select-record",
});
setSourceSaveState({ status: "idle" });
setRecordBrowserState(DEFAULT_SEQUENCE_RECORD_BROWSER_STATE);
setAnnotationIndexState({ expanded: false, page: 0, query: "" });
setOriginRangeExpanded(false);
setChromatogramView({ basesPerWindow: 40, firstBase: 1 });
setReadPileupState(DEFAULT_READ_PILEUP_STATE);
setQualityReport(null);
setQualityAdapterSequence(null);
qualityReportDocumentRef.current = null;
interfaceRestoreEpochRef.current += 1;
setQualityView(createFastqQualityViewState());
}, [initialDocument, sourceStateKey]);
const [interactionState, dispatchInteraction] = useReducer(
sequenceInteractionReducer,
document.records[0]?.id ?? "",
createSequenceInteractionState,
);
const {
activeSearchHitIndex,
focusCoordinate,
selectedFeature,
selectedRecordId,
selection,
} = interactionState;
const setSelectedRecordId = (recordId: string): void =>
dispatchInteraction({ recordId, type: "select-record" });
const setSelectedFeature = (feature: SequenceFeature | undefined): void =>
dispatchInteraction({ feature, type: "set-feature" });
const setFocusCoordinate = (coordinate: number | undefined): void =>
dispatchInteraction({ coordinate, type: "set-focus" });
const setActiveSearchHitIndex = (
value: number | ((current: number) => number),
): void =>
dispatchInteraction({
index: typeof value === "function" ? value(activeSearchHitIndex) : value,
type: "set-search-hit",
});
const [query, setQuery] = useState("");
const [paletteChoice, setPaletteChoice] = useState<{
paletteId: SequencePaletteId;
recordId: string;
restoredFrom?: string;
sourceStateKey: string;
}>(() => ({
paletteId: getDefaultSequencePalette(
initialDocument.records[0]?.molecule ?? "unknown",
),
recordId: initialDocument.records[0]?.id ?? "",
sourceStateKey,
}));
const [wrapWidth, setWrapWidth] = useState(60);
const [showFeatures, setShowFeatures] = useState(true);
const [showQuality, setShowQuality] = useState(true);
const [showTranslation, setShowTranslation] = useState(true);
const [geneticCodeId, setGeneticCodeId] = useState(1);
const [layout, setLayout] = useState<"circular" | "linear" | "split">(
"linear",
);
const [orientation, setOrientation] = useState<
"forward" | "reverse-complement"
>("forward");
const [synchronizedViews, setSynchronizedViews] = useState(true);
const [viewport, setViewport] = useState<{
end: number;
start: number;
} | null>(null);
const [workbenchError, setWorkbenchError] = useState<string>();
const [hoverCoordinate, setHoverCoordinate] = useState<number>();
const [coordinateDraft, setCoordinateDraft] = useState("");
const [recordBrowserState, setRecordBrowserState] = useState(
DEFAULT_SEQUENCE_RECORD_BROWSER_STATE,
);
const [annotationIndexState, setAnnotationIndexState] =
useState<SequenceAnnotationIndexState>({
expanded: false,
page: 0,
query: "",
});
const [originRangeExpanded, setOriginRangeExpanded] = useState(false);
const [chromatogramViewState, setChromatogramView] =
useState<ChromatogramViewState>({
basesPerWindow: 40,
firstBase: 1,
});
const [readPileupStateValue, setReadPileupState] = useState<ReadPileupState>(
DEFAULT_READ_PILEUP_STATE,
);
const [qualityReport, setQualityReport] =
useState<FastqQualityReportState | null>(null);
const [qualityAdapterSequence, setQualityAdapterSequence] = useState<
string | null
>(null);
const [qualityView, setQualityView] = useState(createFastqQualityViewState);
const qualityReportDocumentRef = useRef<SequenceDocument | null>(null);
const interfaceRestoreEpochRef = useRef(0);
const interfaceRestoreEpoch = interfaceRestoreEpochRef.current;
const onQualityReportChange = useCallback(
(next: FastqQualityReportState): void => {
if (next.pending) {
qualityReportDocumentRef.current = workbenchStateRef.current.document;
setQualityAdapterSequence(next.adapterSequence);
}
setQualityReport((current) =>
next.pending || current?.jobId === next.jobId ? next : current,
);
},
[],
);
const currentQualityReport =
sourceDocumentReady && qualityReportDocumentRef.current === document
? qualityReport
: null;
const selectedRecord =
document.records.find(({ id }) => id === selectedRecordId) ??
document.records[0];
const paletteMatchesRecord =
paletteChoice.sourceStateKey === sourceStateKey &&
paletteChoice.recordId === selectedRecord?.id;
const compatiblePalettes = getCompatibleSequencePalettes(
selectedRecord?.molecule ?? "unknown",
);
const paletteId =
paletteMatchesRecord &&
compatiblePalettes.some(({ id }) => id === paletteChoice.paletteId)
? paletteChoice.paletteId
: getDefaultSequencePalette(selectedRecord?.molecule ?? "unknown");
const paletteRestoreNotice =
paletteMatchesRecord && paletteChoice.restoredFrom != null
? `Saved palette "${paletteChoice.restoredFrom}" is unavailable for ${selectedRecord?.molecule ?? "unknown"} sequences; using ${compatiblePalettes.find(({ id }) => id === paletteId)?.label ?? paletteId} instead.`
: null;
const setPaletteId = useCallback(
(nextPaletteId: SequencePaletteId): void => {
setPaletteChoice({
paletteId: nextPaletteId,
recordId: selectedRecord?.id ?? "",
sourceStateKey,
});
},
[selectedRecord?.id, sourceStateKey],
);
const recordBrowser = useMemo(
() =>
normalizeSequenceRecordBrowserState(document.records, recordBrowserState),
[document.records, recordBrowserState],
);
const annotationIndex = useMemo(
() =>
selectedRecord == null
? annotationIndexState
: normalizeSequenceAnnotationIndexState(
selectedRecord,
annotationIndexState,
),
[annotationIndexState, selectedRecord],
);
const changeAnnotationIndex = useCallback(
(patch: Partial<SequenceAnnotationIndexState>): void => {
setAnnotationIndexState((current) => ({ ...current, ...patch }));
},
[],
);
const annotationIndexControl = useMemo(
() => ({ ...annotationIndex, onChange: changeAnnotationIndex }),
[annotationIndex, changeAnnotationIndex],
);
const interfaceRecordRef = useRef(selectedRecord?.id);
const chromatogramView = useMemo<ChromatogramViewState>(() => {
if (interfaceRecordRef.current !== selectedRecord?.id) {
return { basesPerWindow: 40, firstBase: 1 };
}
return {
...chromatogramViewState,
firstBase: Math.min(
chromatogramViewState.firstBase,
Math.max(1, selectedRecord?.length ?? 1),
),
};
}, [chromatogramViewState, selectedRecord?.id, selectedRecord?.length]);
const readPileupState = useMemo(() => {
if (readPileupStateValue.selectedRead == null) return readPileupStateValue;
try {
if (selectedRecord == null)
throw new Error("No reference record is selected.");
getReadPileupEntryForRecord(
workbenchState.tracks,
readPileupStateValue.selectedRead,
selectedRecord,
document.records,
);
return readPileupStateValue;
} catch {
// Source edits, record switches and removed tracks cannot retain a
// projected read selection or serialize it into the next checkpoint.
return { ...readPileupStateValue, selectedRead: null };
}
}, [
document.records,
readPileupStateValue,
selectedRecord,
workbenchState.tracks,
]);
useEffect(() => {
if (interfaceRecordRef.current === selectedRecord?.id) return;
interfaceRecordRef.current = selectedRecord?.id;
setAnnotationIndexState({ expanded: false, page: 0, query: "" });
setOriginRangeExpanded(false);
setChromatogramView({ basesPerWindow: 40, firstBase: 1 });
setReadPileupState((current) => ({ ...current, selectedRead: null }));
}, [selectedRecord?.id]);
useEffect(() => {
if (readPileupState !== readPileupStateValue) {
setReadPileupState((current) =>
current === readPileupStateValue ? readPileupState : current,
);
}
}, [readPileupState, readPileupStateValue]);
const nativeSourceEditClient =
durableViewerState == null
? null
: getNativeSequenceSourceEditClient(durableViewerState.client);
const sourceSaveAvailability = getOriginalSequenceSaveAvailability({
current: document,
initial: initialDocument,
sourceRevision: nativeSourceEditClient?.session.sourceRevision,
});
const saveOriginalSequence = useCallback(async (): Promise<void> => {
if (
nativeSourceEditClient == null ||
!sourceSaveAvailability.available ||
!workbenchState.dirty ||
sourceSaveState.status !== "idle" ||
activeSourceEditRef.current != null
) {
return;
}
const controller = new AbortController();
activeSourceEditRef.current = controller;
let editId: string | undefined;
let committed = false;
setSourceSaveState({ status: "saving" });
setWorkbenchError(undefined);
try {
const originalSource = streamSequenceOriginalSourceReplacement({
client: nativeSourceEditClient,
plan: sourceSaveAvailability.plan,
signal: controller.signal,
});
const firstFragment = await originalSource.next();
if (firstFragment.done || firstFragment.value.byteLength === 0) {
throw new Error("An empty sequence cannot replace the original file.");
}
let lease = await nativeSourceEditClient.beginSourceEdit({
approvedOperation: "replace-source",
expectedSourceRevision: nativeSourceEditClient.session.sourceRevision,
signal: controller.signal,
});
editId = lease.editId;
const maxChunkBytes = Math.min(
lease.maxChunkBytes ?? 64 * 1024,
MAX_SEQUENCE_SOURCE_EDIT_CHUNK_BYTES,
);
const fragments = (async function* () {
yield firstFragment.value;
yield* originalSource;
})();
for await (const chunk of sequenceOriginalSourceUploadChunks({
maxChunkBytes,
signal: controller.signal,
source: fragments,
})) {
const requestId = globalThis.crypto.randomUUID();
for (let attempt = 0; ; attempt += 1) {
try {
if (lease.expiresAtMs - Date.now() <= 30_000) {
lease = await renewOriginalSequenceSourceEdit({
client: nativeSourceEditClient,
editId,
expectedOffsetDecimal: chunk.offsetDecimal,
maxChunkBytes: chunk.bytes.byteLength,
signal: controller.signal,
});
}
lease = await nativeSourceEditClient.appendSourceEdit({
bytes: chunk.bytes,
editId,
offsetDecimal: chunk.offsetDecimal,
requestId,
signal: controller.signal,
});
break;
} catch (error) {
if (
controller.signal.aborted ||
attempt >= 2 ||
!isRecoverableSequenceSourceEditError(error)
) {
throw error;
}
const resumed = await renewOriginalSequenceSourceEdit({
client: nativeSourceEditClient,
editId,
maxChunkBytes: chunk.bytes.byteLength,
signal: controller.signal,
});
if (
resumed.state === "published" ||
(resumed.maxChunkBytes ?? maxChunkBytes) < chunk.bytes.byteLength
) {
throw new Error(
"The original Sequence edit cannot resume its bounded chunk.",
);
}
const durableOffset = BigInt(resumed.bytesWrittenDecimal);
if (
durableOffset ===
BigInt(chunk.offsetDecimal) + BigInt(chunk.bytes.byteLength)
) {
lease = resumed;
break;
}
if (durableOffset !== BigInt(chunk.offsetDecimal)) {
throw new Error(
"The original Sequence edit resumed at an inconsistent offset.",
);
}
lease = resumed;
}
}
}
let result;
try {
if (lease.expiresAtMs - Date.now() <= 30_000) {
lease = await renewOriginalSequenceSourceEdit({
client: nativeSourceEditClient,
editId,
expectedOffsetDecimal: lease.bytesWrittenDecimal,
maxChunkBytes,
signal: controller.signal,
});
}
result = await nativeSourceEditClient.commitSourceEdit({
editId,
signal: controller.signal,
});
} catch (error) {
if (
controller.signal.aborted ||
!isRecoverableSequenceSourceEditError(error)
) {
throw error;
}
const resumed = await nativeSourceEditClient.resumeSourceEdit({
editId,
signal: controller.signal,
});
result =
resumed.state === "published" && resumed.committedResult != null
? resumed.committedResult
: await nativeSourceEditClient.commitSourceEdit({
editId,
signal: controller.signal,
});
}
committed = true;
if (
result.sourceRevision === nativeSourceEditClient.session.sourceRevision
) {
throw new Error(
"The saved sequence must be reopened with its new source revision.",
);
}
durableViewerState?.dispose();
dispatchWorkbench({ dirty: false, type: "set-dirty" });
setSourceSaveState({
message:
"Original sequence saved. Reopen the file to continue with its updated source revision.",
status: "saved",
});
} catch (error) {
if (editId != null && !committed) {
try {
await nativeSourceEditClient.abortSourceEdit({ editId });
} catch {
// The host revokes expired or disconnected source-edit leases.
}
}
setSourceSaveState({
error:
error instanceof Error
? error.message
: "The original sequence file could not be saved.",
status: "idle",
});
} finally {
if (activeSourceEditRef.current === controller) {
activeSourceEditRef.current = null;
}
}
}, [
document,
durableViewerState,
nativeSourceEditClient,
sourceSaveAvailability,
sourceSaveState.status,
workbenchState.dirty,
]);
const effectiveShowQuality = showQuality && selectedRecord?.quality != null;
const effectiveShowTranslation =
showTranslation && recordHasTranslatedCds(selectedRecord);
const orientedRecord = useMemo(
() =>
selectedRecord == null
? undefined
: orientSequenceRecord(selectedRecord, orientation),
[orientation, selectedRecord],
);
const displaySelection = useMemo(
() =>
selectedRecord == null || selection == null
? undefined
: displaySelectionToSource(
selection,
selectedRecord.length,
orientation,
),
[orientation, selectedRecord, selection],
);
const workbenchView = useMemo<SequenceWorkbenchView>(
() => ({
geneticCodeId,
interface: {
annotationIndex,
chromatogram: chromatogramView,
originRangeExpanded,
quality: { adapterSequence: qualityAdapterSequence, view: qualityView },
readPileup: readPileupState,
recordBrowser,
},
layout,
orientation,
paletteId,
selectedFeatureId: selectedFeature?.id ?? null,
selectedRecordId: selectedRecord?.id ?? selectedRecordId,
selection: selection ?? null,
showFeatures,
showQuality: effectiveShowQuality,
showTranslation: effectiveShowTranslation,
synchronizedViews,
viewport,
wrapWidth,
}),
[
annotationIndex,
chromatogramView,
effectiveShowQuality,
effectiveShowTranslation,
geneticCodeId,
layout,
orientation,
originRangeExpanded,
paletteId,
qualityAdapterSequence,
qualityView,
readPileupState,
recordBrowser,
selectedFeature?.id,
selectedRecord?.id,
selectedRecordId,
selection,
showFeatures,
synchronizedViews,
viewport,
wrapWidth,
],
);
const restoreWorkbenchView = useCallback(
(
view: SequenceWorkbenchView,
source?: SequenceWorkbenchRestoreSource,
): void => {
const restoredSource = source ?? workbenchStateRef.current;
const restoredRecord = restoredSource.document.records.find(
({ id }) => id === view.selectedRecordId,
);
if (restoredRecord == null) {
throw new Error(
"The saved selection does not identify a restored source record.",
);
}
const settings = view.interface ?? createSequenceInterfaceSettings();
validateSequenceInterfaceSettingsForSource({
record: restoredRecord,
records: restoredSource.document.records,
settings,
tracks: restoredSource.tracks,
});
const savedPalette = getCompatibleSequencePalettes(
restoredRecord.molecule,
).find(({ id }) => id === view.paletteId);
const restoredPaletteId =
savedPalette?.id ?? getDefaultSequencePalette(restoredRecord.molecule);
interfaceRecordRef.current = restoredRecord.id;
setRecordBrowserState(
normalizeSequenceRecordBrowserState(
restoredSource.document.records,
settings.recordBrowser,
),
);
setAnnotationIndexState(
normalizeSequenceAnnotationIndexState(
restoredRecord,
settings.annotationIndex,
),
);
setChromatogramView(settings.chromatogram);
setOriginRangeExpanded(settings.originRangeExpanded);
setReadPileupState(settings.readPileup);
setQualityView(settings.quality.view);
setQualityAdapterSequence(settings.quality.adapterSequence);
setQualityReport(null);
qualityReportDocumentRef.current = null;
// A restore can run earlier in this render's effects. Do not let that
// render start a report with the previous document or adapter settings.
interfaceRestoreEpochRef.current += 1;
setGeneticCodeId(view.geneticCodeId);
setLayout(view.layout);
setOrientation(view.orientation);
setPaletteChoice({
paletteId: restoredPaletteId,
recordId: restoredRecord.id,
restoredFrom:
savedPalette == null ? view.paletteId.slice(0, 100) : undefined,
sourceStateKey: sourceStateKeyRef.current,
});
setShowFeatures(view.showFeatures);
setShowQuality(view.showQuality);
setShowTranslation(view.showTranslation);
setSynchronizedViews(view.synchronizedViews);
setViewport(view.viewport);
setWrapWidth(view.wrapWidth);
dispatchInteraction({
recordId: view.selectedRecordId,
type: "select-record",
});
if (view.selection == null) {
dispatchInteraction({ type: "clear-selection" });
} else {
dispatchInteraction({
selection: view.selection,
type: "select-range",
});
}
dispatchInteraction({
feature: restoredRecord.features.find(
({ id }) => id === view.selectedFeatureId,
),
type: "set-feature",
});
},
[],
);
const restoredNativeSequence =
durableViewerState?.restoredState?.sourceStateKey === sourceStateKey
? durableViewerState.restoredState.sequence
: undefined;
const [nativeRecoveryReady, setNativeRecoveryReady] = useState(
() => restoredNativeSequence == null,
);
const viewerStateReady = sourceDocumentReady && nativeRecoveryReady;
useEffect(() => {
if (restoredNativeSequence == null || durableViewerState == null) {
setNativeRecoveryReady(true);
return;
}
const checkpointKey =
durableViewerState.client.session.sourceRevision + ":" + sourceStateKey;
if (restoredCheckpointRef.current === checkpointKey) {
setNativeRecoveryReady(true);
return;
}
try {
const session = parseWorkbenchSession(
JSON.stringify(restoredNativeSequence.session),
);
if (
session.view.mode !== "sequence" ||
session.source.stateKey !== sourceStateKey ||
session.source.format !== initialDocument.format ||
session.source.fileName !== (initialDocument.fileName ?? null)
) {
throw new Error(
"The native Sequence checkpoint belongs to a different source artifact.",
);
}
const restoredDocument = createSequenceWorkbenchState(
applySequenceDurableDocumentPatches(
initialDocument,
restoredNativeSequence.documentPatches,
),
initialDocument,
).document;
const restoredRecord = restoredDocument.records.find(
({ id }) => id === restoredNativeSequence.view.selectedRecordId,
);
if (restoredRecord == null) {
throw new Error(
"The native Sequence checkpoint selected an unavailable source record.",
);
}
validateSequenceInterfaceSettingsForSource({
record: restoredRecord,
records: restoredDocument.records,
settings:
restoredNativeSequence.view.interface ??
createSequenceInterfaceSettings(),
tracks: session.tracks,
});
if (
restoredNativeSequence.documentPatches.length > 0 ||
restoredNativeSequence.history.length > 0 ||
restoredNativeSequence.future.length > 0
) {
const historicalDocuments = restoredNativeSequence.history.map(
(entry) => ({
description: entry.description,
document: applySequenceDurableDocumentPatches(
initialDocument,
entry.patches,
),
}),
);
dispatchWorkbench({
document: historicalDocuments[0]?.document ?? initialDocument,
sourceDocument: initialDocument,
type: "reset-sequence-document",
});
for (let index = 1; index < historicalDocuments.length; index++) {
dispatchWorkbench({
description:
historicalDocuments[index - 1]?.description ??
"Automatically restored native sequence history.",
document: historicalDocuments[index]?.document ?? initialDocument,
type: "restore-sequence-document",
});
}
dispatchWorkbench({
description:
historicalDocuments.at(-1)?.description ??
"Automatically restored native sequence edits.",
document: restoredDocument,
type: "restore-sequence-document",
});
for (const entry of [...restoredNativeSequence.future].reverse()) {
dispatchWorkbench({
description: entry.description,
document: applySequenceDurableDocumentPatches(
initialDocument,
entry.patches,
),
type: "restore-sequence-document",
});
}
for (
let index = 0;
index < restoredNativeSequence.future.length;
index++
) {
dispatchWorkbench({ type: "undo-sequence-document" });
}
}
dispatchWorkbench({
state: {
artifacts: session.artifacts,
dirty: session.dirty,
jobs: settleRestoredJobs(session.jobs),
revision: session.revision,
tracks: session.tracks,
},
type: "restore-shared",
});
restoredSearchHitRef.current =
restoredNativeSequence.activeSearchHitIndex;
restoreWorkbenchView(restoredNativeSequence.view, {
document: restoredDocument,
tracks: session.tracks,
});
setQuery(restoredNativeSequence.query);
dispatchInteraction({
coordinate: restoredNativeSequence.focusCoordinate ?? undefined,
type: "set-focus",
});
restoredCheckpointRef.current = checkpointKey;
setWorkbenchError(undefined);
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The native Sequence checkpoint could not be restored.",
);
} finally {
setNativeRecoveryReady(true);
}
}, [
durableViewerState,
initialDocument,
restoreWorkbenchView,
restoredNativeSequence,
sourceStateKey,
]);
useEffect(() => {
if (selectedRecord == null) {
return;
}
setHoverCoordinate(undefined);
setCoordinateDraft("");
}, [selectedRecord]);
const searchResult = useSequenceSearch({ query, record: selectedRecord });
const hits = searchResult.hits;
useEffect(() => {
const restoredHitIndex = restoredSearchHitRef.current;
if (restoredHitIndex != null) {
dispatchInteraction({
index: restoredHitIndex,
type: "set-search-hit",
});
restoredSearchHitRef.current = null;
return;
}
dispatchInteraction({ type: "reset-search" });
}, [query, selectedRecord?.id]);
useEffect(() => {
if (
durableViewerState == null ||
!sourceDocumentReady ||
!nativeRecoveryReady ||
sourceSaveState.status === "saved"
) {
return;
}
try {
durableViewerState.updateSequence({
sourceStateKey,
state: createDurableSequenceState({
activeSearchHitIndex,
focusCoordinate,
initialDocument,
query,
sourceStateKey,
state: workbenchState,
view: workbenchView,
}),
});
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The native Sequence state could not be checkpointed.",
);
}
}, [
activeSearchHitIndex,
durableViewerState,
focusCoordinate,
initialDocument,
nativeRecoveryReady,
query,
sourceSaveState.status,
sourceDocumentReady,
sourceStateKey,
workbenchState,
workbenchView,
]);
useEffect(() => {
if (durableViewerState == null) return;
const flushCheckpoint = (): void => {
void durableViewerState.flush().catch(() => {
// The host retains the previous good checkpoint for automatic retry.
});
};
window.addEventListener("pagehide", flushCheckpoint);
return () => {
window.removeEventListener("pagehide", flushCheckpoint);
flushCheckpoint();
};
}, [durableViewerState]);
useEffect(
() => () => {
activeSourceEditRef.current?.abort(
new DOMException("The Sequence viewer was closed.", "AbortError"),
);
},
[],
);
useSequenceWorkbenchCommands({
cancelledJobsRef,
command:
viewerStateReady && !isSequenceInterfaceCommand(command)
? command
: undefined,
dispatch: dispatchWorkbench,
handledCommandIdRef,
hits: searchResult.hits,
onCommandResult,
onOpenAlignment,
onQualityReportChange,
onRestoreView: restoreWorkbenchView,
selectedRecordId: selectedRecord?.id ?? selectedRecordId,
selection,
sourceStateKey,
state: workbenchState,
view: workbenchView,
});
const sequenceModelContext =
selectedRecord == null
? {
structuredContent: {
artifact: {
fileName: document.fileName ?? null,
format: document.format,
kind: document.kind,
},
viewer: "sequence",
},
text: "Current scientific viewer: Sequence viewer\nState: no biological sequence records were parsed from this file.",
}
: createSequenceViewerModelContext({
activeSearchHitIndex,
artifacts: workbenchState.artifacts,
document,
dirty: workbenchState.dirty,
focusCoordinate,
geneticCodeId,
hits,
jobs: workbenchState.jobs,
layout,
orientation,
paletteId,
query,
record: selectedRecord,
selectedFeature,
selection,
sourceStateKey,
showFeatures,
showQuality: effectiveShowQuality,
showTranslation: effectiveShowTranslation,
synchronizedViews,
tracks: workbenchState.tracks,
viewport,
searchTruncated: searchResult.truncated,
searchPending: searchResult.phase === "searching",
viewerSessionId,
wrapWidth,
});
useModelContext(viewerStateReady ? updateModelContext : undefined, {
...sequenceModelContext,
structuredContent: {
...sequenceModelContext.structuredContent,
interface:
selectedRecord == null
? null
: createSequenceInterfaceSnapshot({
annotationIndex,
chromatogramView,
originRangeExpanded,
paletteId,
paletteRestoreNotice,
qualityAdapterSequence,
qualityReport: currentQualityReport,
qualityView,
readPileupState,
record: selectedRecord,
recordBrowser,
}),
toolbarVisible,
},
});
if (selectedRecord == null) {
return (
<div className="flex min-h-0 flex-1 items-center justify-center p-6 text-token-text-primary">
No biological sequence records were parsed from this file.
</div>
);
}
const searchHitRanges = hits.map((hit) => {
const oriented = orientSearchHit(hit, selectedRecord.length, orientation);
return { end: oriented.end, start: oriented.start };
});
useSequenceInterfaceCommands({
annotationIndex,
chromatogramView,
command: viewerStateReady ? command : undefined,
handledCommandIdRef,
hits,
onAnnotationIndexChange: setAnnotationIndexState,
onChromatogramViewChange: setChromatogramView,
onCommandResult,
onReadPileupStateChange: setReadPileupState,
onRecordBrowserChange: setRecordBrowserState,
onQualityViewChange: setQualityView,
originRangeExpanded,
paletteId,
paletteRestoreNotice,
qualityAdapterSequence,
qualityReport: currentQualityReport,
qualityView,
readPileupState,
record: selectedRecord,
recordBrowser,
selection,
state: workbenchState,
viewport,
});
const selectRange = useCallback((nextSelection: SequenceSelection): void => {
dispatchInteraction({ selection: nextSelection, type: "select-range" });
setCoordinateDraft(nextSelection.start.toString());
setViewport(
nextSelection.segments == null
? {
end: nextSelection.end,
start: nextSelection.start,
}
: null,
);
}, []);
const clearSelection = useCallback((): void => {
dispatchInteraction({ type: "clear-selection" });
}, []);
const selectFeature = useCallback(
(
feature: SequenceFeature,
record: SequenceRecord = selectedRecord,
): SequenceSelection | null => {
const segments = getLocalFeatureSegments(feature).map(
({ end, start }) => ({
end,
start,
}),
);
const first = segments[0];
const last = segments.at(-1);
if (segments.length > 1_000) {
setWorkbenchError(
"Feature selections support up to 1,000 local segments. Select a smaller range to inspect this feature.",
);
return null;
}
if (
first == null ||
last == null ||
segments.some(
({ end, start }) => start < 1 || end < start || end > record.length,
)
) {
setWorkbenchError(
"This feature has no selectable local range in the current record.",
);
return null;
}
const nextSelection: SequenceSelection = {
end: last.end,
recordId: record.id,
...(segments.length > 1 ? { segments } : {}),
start: first.start,
};
setWorkbenchError(undefined);
dispatchInteraction({ feature, type: "set-feature" });
selectRange(nextSelection);
return nextSelection;
},
[selectRange, selectedRecord],
);
const selectSearchHit = useCallback(
(hit: SequenceSearchHit): void => {
selectRange({
end: hit.end,
recordId: hit.recordId,
start: hit.start,
});
},
[selectRange],
);
const selectSearchHitByIndex = useCallback(
(nextIndex: number): { hit: SequenceSearchHit; index: number } | null => {
if (hits.length === 0) {
return null;
}
const normalizedIndex = (nextIndex + hits.length) % hits.length;
setActiveSearchHitIndex(normalizedIndex);
const hit = hits[normalizedIndex];
if (hit == null) {
return null;
}
selectSearchHit(hit);
return { hit, index: normalizedIndex };
},
[hits, selectSearchHit],
);
const selectFeatureByOffset = useCallback(
(
offset: number,
): { feature: SequenceFeature; selection: SequenceSelection } | null => {
if (selectedRecord.features.length === 0) {
return null;
}
const currentIndex =
selectedFeature == null
? offset > 0
? -1
: 0
: selectedRecord.features.findIndex(
({ id }) => id === selectedFeature.id,
);
const normalizedIndex =
(currentIndex + offset + selectedRecord.features.length) %
selectedRecord.features.length;
const feature = selectedRecord.features[normalizedIndex];
if (feature == null) {
return null;
}
const nextSelection = selectFeature(feature);
return nextSelection == null
? null
: { feature, selection: nextSelection };
},
[selectFeature, selectedFeature, selectedRecord],
);
const runLocalAnalysis = useCallback(
(analysis: SequenceViewerAnalysisRequest): void => {
setWorkbenchError(undefined);
runSequenceWorkbenchJob({
analysis,
cancelledJobsRef,
dispatch: dispatchWorkbench,
onQualityReportChange,
selectedRecordId: selectedRecord.id,
selection,
state: workbenchState,
isSourceCurrent: () =>
workbenchStateRef.current.document === workbenchState.document,
viewerGeneticCodeId: geneticCodeId,
});
},
[
geneticCodeId,
onQualityReportChange,
selectedRecord.id,
selection,
workbenchState,
],
);
useEffect(() => {
if (
document.format !== "fastq" ||
!viewerStateReady ||
interfaceRestoreEpoch !== interfaceRestoreEpochRef.current ||
qualityReportDocumentRef.current === document
)
return;
qualityReportDocumentRef.current = document;
runLocalAnalysis({
analysis: "quality-report",
...(qualityAdapterSequence == null
? {}
: { adapterSequence: qualityAdapterSequence }),
});
}, [
document,
qualityAdapterSequence,
interfaceRestoreEpoch,
runLocalAnalysis,
viewerStateReady,
]);
const applyLocalEdit = useCallback(
(request: SequenceViewerEditRequest): void => {
try {
setWorkbenchError(undefined);
const change = applySequenceEditRequest({
document,
request,
selectedRecordId: selectedRecord.id,
});
if ("historyOperation" in change) {
dispatchWorkbench({
type:
change.historyOperation === "undo"
? "undo-sequence-document"
: "redo-sequence-document",
});
return;
}
dispatchWorkbench({
description: change.description,
document: change.document,
type: "apply-sequence-document",
});
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The edit could not be applied.",
);
}
},
[document, selectedRecord.id],
);
const manageLocalAnnotation = useCallback(
(request: SequenceViewerAnnotationRequest): void => {
try {
setWorkbenchError(undefined);
const change = applySequenceAnnotationRequest({
document,
request,
selectedRecordId: selectedRecord.id,
tracks: workbenchState.tracks,
});
dispatchWorkbench({
description: change.description,
document: change.document,
type: "apply-sequence-document",
});
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The annotation change could not be applied.",
);
}
},
[document, selectedRecord.id, workbenchState.tracks],
);
const addLocalAnnotation = useCallback(
({ label, type }: { label: string; type: string }): void => {
if (selection == null || selection.recordId !== selectedRecord.id) return;
manageLocalAnnotation({
action: "add",
feature: {
end: selection.end,
id: `user-${crypto.randomUUID()}`,
label,
qualifiers: { created_by: "sequence-viewer-workbench" },
start: selection.start,
strand: ".",
type,
},
});
},
[manageLocalAnnotation, selectedRecord.id, selection],
);
const loadLocalTrack = useCallback(
async ({
bytes,
displayName,
format,
indexBytes,
indexFormat,
referenceContents,
referenceFileName,
}: {
bytes: Uint8Array;
displayName: string;
format: SequenceTrackFormat;
indexBytes?: Uint8Array;
indexFormat?: "bai" | "csi";
referenceContents?: string;
referenceFileName?: string;
}): Promise<void> => {
try {
setWorkbenchError(undefined);
let sourceItemCount: number | undefined;
let sourceTruncated: boolean | undefined;
let content: string | Uint8Array;
if (format === "cram") {
const decoded = await decodeCramWindowToSam({
cramBytes: bytes,
end: Math.min(selectedRecord.length, 100_000),
indexBytes:
indexBytes ??
(() => {
throw new Error("A CRAI index is required for CRAM tracks.");
})(),
reference: selectedRecord.sourceLabel,
referenceContents,
referenceFileName,
start: 1,
});
content = decoded.sam;
sourceItemCount = decoded.readCount;
sourceTruncated = decoded.truncated;
} else if (format === "bam" && indexBytes != null) {
const decoded = await decodeBamWindowToSam({
bamBytes: bytes,
end: Math.min(selectedRecord.length, 100_000),
indexBytes,
indexFormat,
reference: selectedRecord.sourceLabel,
start: 1,
});
content = decoded.sam;
sourceItemCount = decoded.readCount;
sourceTruncated = decoded.truncated;
} else {
content =
format === "bam"
? await decompressBgzfIfNeeded(bytes)
: decodeUtf8(bytes);
}
const track = parseSequenceTrack({
content,
displayName,
format,
id: crypto.randomUUID(),
requestedReference: selectedRecord.sourceLabel,
sourceItemCount,
sourceTruncated,
});
dispatchWorkbench({ track, type: "add-track" });
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The track could not be loaded.",
);
}
},
[selectedRecord.length, selectedRecord.sourceLabel],
);
const alignLocalRecords = useCallback((): void => {
const sourceDocument = document;
const jobId = crypto.randomUUID();
dispatchWorkbench({
job: {
id: jobId,
kind: "align",
message: "Aligning materialized records…",
parameters: { recordIds: document.records.map(({ id }) => id) },
progress: 0,
startedAt: Date.now(),
status: "running",
},
type: "start-job",
});
queueMicrotask(() => {
try {
if (cancelledJobsRef.current.has(jobId)) return;
if (workbenchStateRef.current.document !== sourceDocument) {
dispatchWorkbench({
completedAt: Date.now(),
error:
"The source sequence changed before this job completed. Run it again on the current copy.",
id: jobId,
type: "fail-job",
});
return;
}
const aligned = alignSequences(
sourceDocument.records.map(
({ description, id, sequence, sourceLabel }) => ({
description,
id,
label: sourceLabel,
sequence,
}),
),
);
if (cancelledJobsRef.current.has(jobId)) return;
if (workbenchStateRef.current.document !== sourceDocument) {
dispatchWorkbench({
completedAt: Date.now(),
error:
"The source sequence changed before this job completed. Run it again on the current copy.",
id: jobId,
type: "fail-job",
});
return;
}
const content = `${exportAlignedFasta(aligned.rows)}\n`;
const name = `${stripFileExtension(sourceDocument.fileName ?? "sequences")}.aligned.fasta`;
dispatchWorkbench({
artifact: {
content,
createdAt: Date.now(),
format: "aligned-fasta",
id: crypto.randomUUID(),
mediaType: "text/x-fasta",
name,
provenance: {
engine: aligned.engine,
parameters: aligned.parameters,
sourceRevision: workbenchState.revision,
},
},
type: "add-artifact",
});
dispatchWorkbench({
completedAt: Date.now(),
id: jobId,
message: `Aligned ${aligned.rows.length.toLocaleString()} records.`,
result: {
alignedLength: aligned.alignedLength,
engine: aligned.engine,
rowCount: aligned.rows.length,
warning: aligned.warning,
},
type: "complete-job",
});
onOpenAlignment?.(content, name);
} catch (error) {
if (cancelledJobsRef.current.has(jobId)) return;
dispatchWorkbench({
completedAt: Date.now(),
error:
error instanceof Error
? error.message
: "The records could not be aligned.",
id: jobId,
type: "fail-job",
});
}
});
}, [
document.fileName,
document.records,
onOpenAlignment,
workbenchState.revision,
]);
const exportLocalArtifact = useCallback(
(
format:
| "bed"
| "csv"
| "embl"
| "fasta"
| "fastq"
| "genbank"
| "gff3"
| "gtf"
| "json"
| "pdf"
| "svg"
| "tsv"
| "vcf",
scope: "all" | "selection" | "visible",
): void => {
try {
setWorkbenchError(undefined);
const output = exportSequenceWorkbench({
document,
format,
recordId: selectedRecord.id,
scope,
selection,
tracks: workbenchState.tracks,
});
dispatchWorkbench({
artifact: {
...output,
createdAt: Date.now(),
id: crypto.randomUUID(),
provenance: {
engine: "sequence-viewer-export-v1",
parameters: { scope },
sourceRevision: workbenchState.revision,
},
},
type: "add-artifact",
});
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The export could not be prepared.",
);
}
},
[
document,
selectedRecord.id,
selection,
workbenchState.revision,
workbenchState.tracks,
],
);
const prepareWorkspaceSession = useCallback(
(): string =>
serializeWorkbenchSession(
createSequenceWorkbenchSession(
workbenchState,
workbenchView,
sourceStateKey,
),
),
[sourceStateKey, workbenchState, workbenchView],
);
const saveLocalSession = useCallback((): void => {
try {
setWorkbenchError(undefined);
const content = prepareWorkspaceSession();
dispatchWorkbench({
artifact: {
content,
createdAt: Date.now(),
format: "sequence-viewer-session",
id: crypto.randomUUID(),
mediaType: "application/json",
name: `${stripFileExtension(document.fileName ?? "sequence")}.sequence-session.json`,
provenance: {
engine: "sequence-viewer-session-v1",
parameters: { mode: "sequence" },
sourceRevision: workbenchState.revision,
},
},
type: "add-artifact",
});
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The session could not be saved.",
);
}
}, [document.fileName, prepareWorkspaceSession, workbenchState.revision]);
const restoreLocalSession = useCallback(
(content: string): void => {
try {
setWorkbenchError(undefined);
const session = parseWorkbenchSession(content);
if (session.view.mode !== "sequence" || session.view.sequence == null) {
throw new Error("This is not a Sequence-mode workbench session.");
}
if (
session.source.format !== document.format ||
session.source.fileName !== (document.fileName ?? null) ||
session.source.stateKey !== sourceStateKey
) {
throw new Error(
"This session belongs to a different source artifact and was not applied.",
);
}
const restoredDocument =
session.snapshot?.sequenceDocument == null
? document
: createSequenceWorkbenchState(
session.snapshot.sequenceDocument,
workbenchState.sourceDocument,
).document;
restoreWorkbenchView(session.view.sequence, {
document: restoredDocument,
tracks: session.tracks,
});
if (session.snapshot?.sequenceDocument != null) {
dispatchWorkbench({
description: "Restored saved sequence-copy state.",
document: restoredDocument,
type: "restore-sequence-document",
});
}
dispatchWorkbench({
state: {
artifacts: session.artifacts,
dirty: session.dirty,
jobs: settleRestoredJobs(session.jobs),
revision: workbenchState.revision,
tracks: session.tracks,
},
type: "restore-shared",
});
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The session could not be restored.",
);
}
},
[document, restoreWorkbenchView, sourceStateKey, workbenchState],
);
useEffect(() => {
if (
command == null ||
!viewerStateReady ||
command.action === "set_mode" ||
isWorkbenchOperationCommand(command) ||
isSequenceInterfaceCommand(command) ||
handledCommandIdRef.current === command.commandId
) {
return;
}
const requestedRecord = "record" in command ? command.record : undefined;
const recordResolution =
requestedRecord == null
? { status: "resolved" as const, target: selectedRecord }
: resolveViewerTarget({
aliases: (record) => [record.sourceLabel, record.description],
id: (record) => record.id,
selector: requestedRecord,
targets: document.records,
});
if (
recordResolution.status !== "resolved" ||
recordResolution.target == null
) {
handledCommandIdRef.current = command.commandId;
onCommandResult?.(command, {
applied: false,
message:
recordResolution.status === "ambiguous"
? `More than one sequence record matched ${requestedRecord}; retry with an exact record ID.`
: `No sequence record matched ${requestedRecord ?? "the request"}.`,
state:
recordResolution.status === "ambiguous"
? {
candidateRecordIds: recordResolution.candidates.map(
({ id }) => id,
),
}
: undefined,
});
return;
}
const targetRecord = recordResolution.target;
if (targetRecord.id !== selectedRecordId) {
setSelectedRecordId(targetRecord.id);
return;
}
let result: SequenceViewerCommandResult;
switch (command.action) {
case "clear_sequence_selection":
clearSelection();
result = {
applied: true,
message: "Cleared the sequence selection.",
};
break;
case "focus_sequence_coordinate": {
const coordinate = command.coordinate ?? 1;
if (coordinate < 1 || coordinate > targetRecord.length) {
result = {
applied: false,
message: `Coordinate ${coordinate} is outside ${targetRecord.sourceLabel} (1-${targetRecord.length}).`,
state: { maxCoordinate: targetRecord.length, minCoordinate: 1 },
};
break;
}
selectRange({
end: coordinate,
recordId: targetRecord.id,
start: coordinate,
});
result = {
applied: true,
message: `Focused ${targetRecord.sourceLabel} at coordinate ${coordinate}.`,
state: { coordinate, recordId: targetRecord.id },
};
break;
}
case "navigate_sequence_search_hit": {
const selected = selectSearchHitByIndex(
activeSearchHitIndex + (command.direction === "next" ? 1 : -1),
);
if (selected == null) {
result = {
applied: false,
message: "There are no sequence search hits to navigate.",
};
break;
}
result = {
applied: true,
message: `Focused sequence search hit ${selected.index + 1} of ${hits.length}.`,
state: {
end: selected.hit.end,
hitCount: hits.length,
hitIndex: selected.index + 1,
orientation: selected.hit.orientation,
recordId: selected.hit.recordId,
start: selected.hit.start,
},
};
break;
}
case "select_sequence_range": {
const requestedStart = command.start ?? 1;
const requestedEnd = command.end ?? 1;
const start = command.wraparound
? requestedStart
: Math.min(requestedStart, requestedEnd);
const end = command.wraparound
? requestedEnd
: Math.max(requestedStart, requestedEnd);
if (
start < 1 ||
end < 1 ||
start > targetRecord.length ||
end > targetRecord.length
) {
result = {
applied: false,
message: `Range ${start}-${end} is outside ${targetRecord.sourceLabel} (1-${targetRecord.length}).`,
state: { maxCoordinate: targetRecord.length, minCoordinate: 1 },
};
break;
}
let nextSelection: SequenceSelection;
try {
nextSelection = command.wraparound
? createOriginSpanningSelection({
end,
record: targetRecord,
start,
})
: { end, recordId: targetRecord.id, start };
} catch (error) {
result = {
applied: false,
message:
error instanceof Error
? error.message
: "The origin-spanning range is invalid.",
};
break;
}
selectRange(nextSelection);
result = {
applied: true,
message: `Selected ${targetRecord.sourceLabel} coordinates ${start}-${end}${command.wraparound ? " across the circular origin" : ""}.`,
state: command.wraparound
? {
end,
recordId: targetRecord.id,
segments: nextSelection.segments,
start,
wraparound: true,
}
: { end, recordId: targetRecord.id, start },
};
break;
}
case "select_sequence_feature": {
const featureMatches = resolveSequenceFeatureSelector(
targetRecord.features,
command.featureId,
);
if (featureMatches.length === 0) {
const availableFeatures = targetRecord.features
.slice(0, 50)
.map(({ end, id, label, start, type }) => ({
end,
id,
label: label ?? null,
start,
type,
}));
result = {
applied: false,
message: `No feature in ${targetRecord.sourceLabel} matched ${command.featureId}.`,
state: {
availableFeatures,
availableFeaturesTruncated:
targetRecord.features.length > availableFeatures.length,
},
};
break;
}
if (featureMatches.length > 1) {
const matchingFeatures = featureMatches
.slice(0, 50)
.map(({ end, id, label, start, type }) => ({
end,
id,
label: label ?? null,
start,
type,
}));
result = {
applied: false,
message: `More than one feature matched ${command.featureId}; retry with one of the exact candidate IDs.`,
state: {
matchingFeatures,
matchingFeaturesTruncated:
featureMatches.length > matchingFeatures.length,
},
};
break;
}
const feature = featureMatches[0];
if (feature == null) {
return;
}
const featureSelection = selectFeature(feature, targetRecord);
if (featureSelection == null) {
result = {
applied: false,
message: `The local ranges of ${feature.label ?? feature.type} cannot be selected in ${targetRecord.sourceLabel}.`,
state: { featureId: feature.id, recordId: targetRecord.id },
};
break;
}
result = {
applied: true,
message: `Selected ${feature.label ?? feature.type} in ${targetRecord.sourceLabel}.`,
state: {
...featureSelection,
featureId: feature.id,
type: feature.type,
},
};
break;
}
case "navigate_sequence_feature": {
const next = selectFeatureByOffset(
command.direction === "next" ? 1 : -1,
);
result =
next == null
? {
applied: false,
message:
"No selectable local feature is available in this record.",
}
: {
applied: true,
message: `Selected ${next.feature.label ?? next.feature.type}.`,
state: { ...next.selection, featureId: next.feature.id },
};
break;
}
case "set_sequence_record":
result = {
applied: true,
message: `Displayed sequence record ${targetRecord.sourceLabel}.`,
state: { recordId: targetRecord.id },
};
break;
case "set_sequence_view_options": {
if (
command.geneticCodeId != null &&
getGeneticCode(command.geneticCodeId) == null
) {
result = {
applied: false,
message: `Genetic code table ${command.geneticCodeId} is not supported.`,
};
break;
}
if (
command.orientation === "reverse-complement" &&
(targetRecord.molecule === "protein" ||
targetRecord.molecule === "unknown")
) {
result = {
applied: false,
message: `${targetRecord.sourceLabel} cannot be shown as a reverse complement because it is ${targetRecord.molecule}.`,
};
break;
}
if (
command.palette != null &&
!getCompatibleSequencePalettes(targetRecord.molecule).some(
({ id }) => id === command.palette,
)
) {
result = {
applied: false,
message: `Palette ${command.palette} is not compatible with ${targetRecord.molecule} sequences.`,
};
break;
}
if (command.showQuality === true && targetRecord.quality == null) {
result = {
applied: false,
message: `${targetRecord.sourceLabel} does not contain FASTQ quality scores.`,
};
break;
}
if (
command.showTranslation === true &&
!targetRecord.features.some(
({ translation, type }) =>
type.toLowerCase() === "cds" && translation != null,
)
) {
result = {
applied: false,
message: `${targetRecord.sourceLabel} does not contain a translated CDS feature.`,
};
break;
}
if (command.palette != null) {
setPaletteId(command.palette);
}
if (command.geneticCodeId != null) {
setGeneticCodeId(command.geneticCodeId);
}
if (command.layout != null) {
setLayout(command.layout);
}
if (command.orientation != null) {
setOrientation(command.orientation);
}
if (command.showFeatures != null) {
setShowFeatures(command.showFeatures);
}
if (command.showQuality != null) {
setShowQuality(command.showQuality);
}
if (command.showTranslation != null) {
setShowTranslation(command.showTranslation);
}
if (command.synchronizedViews != null) {
setSynchronizedViews(command.synchronizedViews);
}
if (command.originRangeExpanded != null) {
setOriginRangeExpanded(command.originRangeExpanded);
}
if (command.wrapWidth != null) {
setWrapWidth(command.wrapWidth);
}
result = {
applied: true,
message: `Updated display options for ${targetRecord.sourceLabel}.`,
state: {
geneticCodeId: command.geneticCodeId ?? geneticCodeId,
layout: command.layout ?? layout,
orientation: command.orientation ?? orientation,
originRangeExpanded:
command.originRangeExpanded ?? originRangeExpanded,
palette: command.palette ?? paletteId,
showFeatures: command.showFeatures ?? showFeatures,
showQuality:
targetRecord.quality != null &&
(command.showQuality ?? showQuality),
showTranslation:
recordHasTranslatedCds(targetRecord) &&
(command.showTranslation ?? showTranslation),
synchronizedViews: command.synchronizedViews ?? synchronizedViews,
wrapWidth: command.wrapWidth ?? wrapWidth,
},
};
break;
}
case "search_sequence": {
const nextQuery = command.query ?? "";
if (query !== nextQuery) {
setQuery(nextQuery);
setActiveSearchHitIndex(0);
return;
}
if (searchResult.phase === "searching") return;
const nextHits = searchResult.hits;
const firstHit = nextHits[0];
if (firstHit != null) {
selectRange({
end: firstHit.end,
recordId: firstHit.recordId,
start: firstHit.start,
});
}
result = {
applied: true,
message:
nextHits.length === 0
? searchResult.truncated
? `Search of ${targetRecord.sourceLabel} reached the bounded work limit without finding a match; refine the query and retry.`
: `Searched ${targetRecord.sourceLabel}; no matches were found.`
: `Found ${nextHits.length} match${nextHits.length === 1 ? "" : "es"} and focused the first one.`,
state: {
hitCount: nextHits.length,
query: nextQuery,
recordId: targetRecord.id,
truncated: searchResult.truncated,
},
};
break;
}
default:
return;
}
handledCommandIdRef.current = command.commandId;
onCommandResult?.(command, result);
}, [
activeSearchHitIndex,
clearSelection,
command,
document.records,
geneticCodeId,
hits.length,
layout,
onCommandResult,
orientation,
originRangeExpanded,
paletteId,
query,
searchResult,
selectRange,
selectSearchHitByIndex,
selectedRecord,
selectedRecordId,
setPaletteId,
selectFeature,
selectFeatureByOffset,
showFeatures,
showQuality,
showTranslation,
viewerStateReady,
synchronizedViews,
wrapWidth,
]);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent): void => {
if (event.key === "Escape" && selection != null) {
clearSelection();
}
};
window.addEventListener("keydown", handleKeyDown);
return (): void => window.removeEventListener("keydown", handleKeyDown);
}, [clearSelection, selection]);
return (
<div className="bio-sequence-surface flex min-h-0 flex-1 flex-col bg-token-main-surface-primary text-token-text-primary">
<div
data-sequence-toolbar-controls="sequence"
hidden={!(toolbarRevealed ?? toolbarVisible)}
inert={!(toolbarRevealed ?? toolbarVisible) ? true : undefined}
>
<SequenceToolbar
coordinateValue={coordinateDraft}
document={document}
displayOptions={
<div className="flex flex-wrap items-center gap-4">
<button
aria-pressed={orientation === "reverse-complement"}
className="rounded-md border border-token-border px-2.5 py-1 text-xs text-token-text-secondary hover:text-token-text-primary"
disabled={
selectedRecord.molecule === "protein" ||
selectedRecord.molecule === "unknown"
}
onClick={() =>
setOrientation((current) =>
current === "forward" ? "reverse-complement" : "forward",
)
}
type="button"
>
{orientation === "forward"
? "Show reverse complement"
: "Show forward strand"}
</button>
<label className="inline-flex items-center gap-1.5 text-xs text-token-text-secondary">
<input
checked={synchronizedViews}
onChange={(event) =>
setSynchronizedViews(event.target.checked)
}
type="checkbox"
/>
Sync overview and detail
</label>
</div>
}
featureCount={selectedRecord.features.length}
onCoordinateChange={setCoordinateDraft}
onCoordinateJump={() => {
const coordinate = clampSequenceCoordinate(
Number(coordinateDraft),
selectedRecord.length,
);
const nextSelection = {
end: coordinate,
recordId: selectedRecord.id,
start: coordinate,
};
selectRange(nextSelection);
}}
onClearSelection={clearSelection}
onNextFeature={() => selectFeatureByOffset(1)}
onNextSearchHit={() =>
selectSearchHitByIndex(activeSearchHitIndex + 1)
}
onPaletteChange={setPaletteId}
onPreviousFeature={() => selectFeatureByOffset(-1)}
onPreviousSearchHit={() =>
selectSearchHitByIndex(activeSearchHitIndex - 1)
}
publishWorkspaceArtifact={publishWorkspaceArtifact}
onQueryChange={setQuery}
onToggleFeatures={() => setShowFeatures((value) => !value)}
onToggleQuality={() => setShowQuality((value) => !value)}
onToggleTranslation={() => setShowTranslation((value) => !value)}
onWrapWidthChange={setWrapWidth}
paletteId={paletteId}
query={query}
record={selectedRecord}
searchHitCount={hits.length}
selection={selection}
showFeatures={showFeatures}
showQuality={effectiveShowQuality}
showTranslation={effectiveShowTranslation}
sourceRevision={workbenchState.revision}
wrapWidth={wrapWidth}
/>
</div>
<PerformanceBanners document={document} />
{paletteRestoreNotice == null ? null : (
<p
className="px-4 py-2 text-sm text-token-text-secondary"
data-workbench-nonblocking="true"
role="status"
>
{paletteRestoreNotice}
</p>
)}
<WarningsDrawer warnings={document.warnings} />
<div className="bio-workbench-layout grid min-h-0 flex-1 gap-4 p-4 xl:grid-cols-[minmax(0,1fr)_20rem]">
<main
className="bio-workbench-main min-w-0 space-y-4"
aria-label="Sequence canvas"
>
<RecordListPanel
browserState={recordBrowser}
onBrowserStateChange={setRecordBrowserState}
onSelectRecord={(recordId) => {
setSelectedRecordId(recordId);
}}
records={document.records}
selectedRecordId={selectedRecord.id}
totalRecordCount={
document.recordInventory?.totalCount ?? document.records.length
}
/>
<div className="bio-sequence-viewbar flex flex-wrap items-center gap-2 border-b border-token-border px-1 pb-3">
<span className="text-sm font-medium text-token-text-primary">
View
</span>
{(["linear", "circular", "split"] as const).map((candidate) => (
<button
aria-pressed={layout === candidate}
className={
layout === candidate
? "rounded-md border border-token-border bg-token-main-surface-primary px-2.5 py-1 text-sm font-medium text-token-text-primary shadow-sm"
: "rounded-md border border-token-border px-2.5 py-1 text-sm text-token-text-secondary hover:text-token-text-primary"
}
key={candidate}
onClick={() => setLayout(candidate)}
type="button"
>
{candidate[0]?.toUpperCase()}
{candidate.slice(1)}
</button>
))}
{nativeSourceEditClient == null ? null : (
<Button
aria-label="Save Original"
disabled={
!workbenchState.dirty ||
!sourceSaveAvailability.available ||
sourceSaveState.status !== "idle"
}
onClick={() => {
void saveOriginalSequence();
}}
title={
sourceSaveState.status === "saved"
? "Reopen the saved source before making further changes."
: !workbenchState.dirty
? "Make a sequence edit before saving the original file."
: sourceSaveAvailability.reason
}
type="button"
>
{sourceSaveState.status === "saving"
? "Saving Original…"
: sourceSaveState.status === "saved"
? "Original Saved"
: "Save Original"}
</Button>
)}
</div>
{sourceSaveState.message == null ? null : (
<div
className="rounded-lg border border-emerald-500/40 bg-emerald-500/5 px-3 py-2 text-xs text-emerald-700 dark:text-emerald-400"
role="status"
>
{sourceSaveState.message}
</div>
)}
{workbenchError == null ? null : (
<div
className="rounded-lg border border-red-500/40 bg-red-500/5 px-3 py-2 text-xs text-red-600 dark:text-red-400"
data-workbench-nonblocking="true"
role="alert"
>
{workbenchError}
</div>
)}
{sourceSaveState.error == null ? null : (
<div
className="rounded-lg border border-red-500/40 bg-red-500/5 px-3 py-2 text-xs text-red-600 dark:text-red-400"
role="alert"
>
{sourceSaveState.error}
</div>
)}
<SequenceOverview
annotationIndex={annotationIndexControl}
layout={layout}
onOriginRangeExpandedChange={setOriginRangeExpanded}
originRangeExpanded={originRangeExpanded}
onJump={(coordinate) =>
selectRange({
end: coordinate,
recordId: selectedRecord.id,
start: coordinate,
})
}
onSelectRange={(start, end, wraparound) => {
try {
setWorkbenchError(undefined);
selectRange(
wraparound
? createOriginSpanningSelection({
end,
record: selectedRecord,
start,
})
: {
end: Math.max(start, end),
recordId: selectedRecord.id,
start: Math.min(start, end),
},
);
} catch (error) {
setWorkbenchError(
error instanceof Error
? error.message
: "The range could not be selected.",
);
}
}}
onSelectFeature={(featureId) => {
const feature = selectedRecord.features.find(
({ id }) => id === featureId,
);
if (feature == null) return;
selectFeature(feature);
}}
orientation={orientation}
record={selectedRecord}
selectedFeatureId={selectedFeature?.id}
selection={selection}
synchronizedViews={synchronizedViews}
viewport={viewport}
/>
{selectedRecord.chromatogram == null ? null : (
<ChromatogramPanel
record={selectedRecord}
restorationEpoch={interfaceRestoreEpoch}
selection={selection}
view={chromatogramView}
onViewChange={setChromatogramView}
onSelectRange={(start, end) =>
selectRange({
end,
recordId: selectedRecord.id,
start,
})
}
/>
)}
<EvidenceTrackPanel
paletteId={paletteId}
readPileupState={readPileupState}
onReadPileupStateChange={setReadPileupState}
record={selectedRecord}
referenceRecords={document.records}
selection={selection}
tracks={workbenchState.tracks}
viewport={viewport}
/>
<SequenceRenderer
focusCoordinate={
focusCoordinate == null
? undefined
: sourceCoordinateToDisplay(
focusCoordinate,
selectedRecord.length,
orientation,
)
}
lineWidth={wrapWidth}
onFocusCoordinate={(coordinate) => {
const sourceCoordinate = sourceCoordinateToDisplay(
coordinate,
selectedRecord.length,
orientation,
);
setFocusCoordinate(sourceCoordinate);
setCoordinateDraft(sourceCoordinate.toString());
}}
onHoverCoordinate={(coordinate) => {
if (coordinate == null) {
setHoverCoordinate(undefined);
return;
}
const sourceCoordinate = sourceCoordinateToDisplay(
coordinate,
selectedRecord.length,
orientation,
);
setHoverCoordinate(sourceCoordinate);
setCoordinateDraft(sourceCoordinate.toString());
}}
onSelectFeature={(feature) => {
const sourceFeature = selectedRecord.features.find(
({ id }) => id === feature.id,
);
if (sourceFeature == null) return;
selectFeature(sourceFeature);
}}
onSelectionChange={(nextSelection) =>
selectRange(
displaySelectionToSource(
nextSelection,
selectedRecord.length,
orientation,
),
)
}
paletteId={paletteId}
record={orientedRecord ?? selectedRecord}
searchHitRanges={searchHitRanges}
selectedFeatureId={selectedFeature?.id}
selection={displaySelection}
showFeatures={showFeatures}
showQuality={effectiveShowQuality}
showTranslation={effectiveShowTranslation}
/>
<SequenceLegend paletteId={paletteId} />
</main>
<aside
className="bio-workbench-sidebar min-w-0"
aria-label="Sequence workspace tools"
>
<SequenceWorkbenchPanel
browseWorkspaceTracks={browseWorkspaceTracks}
contextPanels={[
{
id: "inspect",
label: "Inspect",
content: (
<div className="space-y-4">
<PinnedInspector
feature={selectedFeature}
hoverCoordinate={hoverCoordinate}
onClearSelection={clearSelection}
record={selectedRecord}
selection={selection}
/>
<MetadataPanel record={selectedRecord} />
</div>
),
},
...(document.fastqSummary == null &&
selectedRecord.quality == null
? []
: [
{
id: "quality",
label: "Quality",
content: (
<div className="space-y-4">
<FastqSummaryPanel
adapterSequence={qualityAdapterSequence}
document={document}
onAdapterSequenceApply={(adapterSequence) =>
runLocalAnalysis({
analysis: "quality-report",
...(adapterSequence == null
? {}
: { adapterSequence }),
})
}
qualityReport={currentQualityReport?.report ?? null}
qualityReportError={currentQualityReport?.error}
qualityReportPending={
currentQualityReport == null ||
currentQualityReport.pending
}
onViewChange={setQualityView}
view={qualityView}
summary={document.fastqSummary}
/>
{selectedRecord.quality == null ? null : (
<QualityTrack
record={orientedRecord ?? selectedRecord}
/>
)}
</div>
),
},
]),
...(query.length === 0
? []
: [
{
id: "search-results",
label: `Results (${hits.length.toLocaleString()}${searchResult.truncated ? "+" : ""})`,
content: (
<SearchPanel
hits={hits}
onSelectHit={selectSearchHit}
searching={searchResult.phase === "searching"}
truncated={searchResult.truncated}
/>
),
},
]),
]}
geneticCodeId={geneticCodeId}
onAddAnnotation={addLocalAnnotation}
onAlignRecords={alignLocalRecords}
onCancelJob={(jobId) => {
cancelledJobsRef.current.add(jobId);
dispatchWorkbench({ id: jobId, type: "cancel-job" });
}}
onEdit={applyLocalEdit}
onExport={exportLocalArtifact}
onGeneticCodeChange={setGeneticCodeId}
onDeleteAnnotation={(featureId) => {
manageLocalAnnotation({ action: "delete", featureId });
if (selectedFeature?.id === featureId)
setSelectedFeature(undefined);
}}
onImportTrack={(trackId) =>
manageLocalAnnotation({ action: "import", trackId })
}
onLoadTrack={(input) => void loadLocalTrack(input)}
onRedo={() => dispatchWorkbench({ type: "redo-sequence-document" })}
onRemoveTrack={(id) =>
dispatchWorkbench({ id, type: "remove-track" })
}
onRunAnalysis={runLocalAnalysis}
onRestoreSession={restoreLocalSession}
onSaveSession={saveLocalSession}
prepareWorkspaceSession={prepareWorkspaceSession}
publishWorkspaceArtifact={publishWorkspaceArtifact}
workspaceSessionName={workspaceSessionDefaultName(
document.fileName,
"sequence",
)}
workspaceSessions={workspaceSessions}
onSelectFeature={(feature) => {
selectFeature(feature);
}}
onUndo={() => dispatchWorkbench({ type: "undo-sequence-document" })}
onUpdateAnnotation={({ feature, label, type }) =>
manageLocalAnnotation({
action: "update",
feature: {
end: feature.end,
id: feature.id,
label,
qualifiers: feature.qualifiers,
start: feature.start,
strand: feature.strand,
type,
},
})
}
record={selectedRecord}
recordCount={document.records.length}
selection={selection}
selectedFeature={selectedFeature}
state={workbenchState}
/>
</aside>
</div>
</div>
);
}
type NativeSequenceSourceEditClient = Pick<
ScientificSequenceDataClient,
| "session"
| "readOriginalSourceRange"
| "beginSourceEdit"
| "appendSourceEdit"
| "resumeSourceEdit"
| "commitSourceEdit"
| "abortSourceEdit"
>;
function getNativeSequenceSourceEditClient(
client: unknown,
): NativeSequenceSourceEditClient | null {
if (client == null || typeof client !== "object") return null;
const candidate = client as Partial<NativeSequenceSourceEditClient>;
return candidate.session != null &&
candidate.session.canEditApprovedSource === true &&
typeof candidate.readOriginalSourceRange === "function" &&
typeof candidate.beginSourceEdit === "function" &&
typeof candidate.appendSourceEdit === "function" &&
typeof candidate.resumeSourceEdit === "function" &&
typeof candidate.commitSourceEdit === "function" &&
typeof candidate.abortSourceEdit === "function"
? (candidate as NativeSequenceSourceEditClient)
: null;
}
function isRecoverableSequenceSourceEditError(error: unknown): boolean {
if (!(error instanceof Error)) return false;
const code =
"code" in error && typeof error.code === "string" ? error.code : "";
const message = `${code} ${error.message}`;
if (
/(?:source.changed|revok|not.authoriz|scope|conflict|digest|offset|abort)/iu.test(
message,
)
) {
return false;
}
if (/permission/iu.test(message) && !/expir/iu.test(message)) return false;
return /(?:stale.?generation|expir|disconnect|unavailable|temporar|timeout|closed|econnreset)/iu.test(
message,
);
}
async function renewOriginalSequenceSourceEdit({
client,
editId,
expectedOffsetDecimal,
maxChunkBytes,
signal,
}: {
client: NativeSequenceSourceEditClient;
editId: string;
expectedOffsetDecimal?: string;
maxChunkBytes: number;
signal?: AbortSignal;
}) {
const resumed = await client.resumeSourceEdit({ editId, signal });
if (
resumed.state === "published" ||
(resumed.maxChunkBytes ?? 64 * 1024) < maxChunkBytes ||
(expectedOffsetDecimal != null &&
resumed.bytesWrittenDecimal !== expectedOffsetDecimal)
) {
throw new Error(
"The original Sequence edit cannot renew its authenticated source lease.",
);
}
return resumed;
}
function getOriginalSequenceSaveAvailability({
current,
initial,
sourceRevision,
}: {
current: SequenceDocument;
initial: SequenceDocument;
sourceRevision?: string;
}):
| {
available: true;
plan: SequenceOriginalSourceReplacementPlan;
reason: string;
}
| { available: false; reason: string } {
if (sourceRevision == null) {
return {
available: false,
reason:
"Original-source replacement requires an authenticated native reader.",
};
}
try {
const plan = createSequenceOriginalSourceReplacementPlan({
current,
initial,
sourceRevision,
});
return {
available: true,
plan,
reason:
"Replace only the edited residues while preserving every other original source byte.",
};
} catch (error) {
return {
available: false,
reason:
error instanceof Error
? error.message
: "The original Sequence source cannot be safely replaced.",
};
}
}
function recordHasTranslatedCds(
record: SequenceDocument["records"][number] | undefined,
): boolean {
return (
record?.features.some(
({ translation, type }) =>
type.toLowerCase() === "cds" && translation != null,
) ?? false
);
}
function decodeUtf8(bytes: Uint8Array): string {
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
}
function stripFileExtension(fileName: string): string {
const normalized = fileName.trim();
const index = normalized.lastIndexOf(".");
return index > 0 ? normalized.slice(0, index) : normalized || "sequences";
}
async function decompressBgzfIfNeeded(bytes: Uint8Array): Promise<Uint8Array> {
if (bytes[0] !== 0x1f || bytes[1] !== 0x8b) {
return bytes;
}
if (typeof DecompressionStream === "undefined") {
throw new Error(
"This browser cannot decompress BGZF BAM data. Load the track through sequence.load_track instead.",
);
}
const copy = bytes.slice().buffer;
const stream = new Blob([copy])
.stream()
.pipeThrough(new DecompressionStream("gzip"));
return new Uint8Array(await new Response(stream).arrayBuffer());
}
SHA-256: 98a4bd2b9b0f0a39c718c2633c701292c576891df4980a0fe06b2dc372b8742d