2026-07-04 — v27 clone-screen stacked viewer: synchronized multi-trace comparison
The classic Sanger verification loop goes like this: you sequence the same insert with forward and reverse primers (or repeat the run with a second colony), and then you carefully switch between the two chromatogram windows looking for any position where the base call disagrees. One mismatch in a 700-base trace and the entire insert is suspect. It is tedious, error-prone, and — until today — exactly how you had to do it with sanger-viewer.
Step 27 ships the clone-screen stacked viewer: load two or more traces and they line up base-by-base in a panel below the chromatogram. Agreement columns show teal, mismatch columns flash amber, and a synchronized cursor tracks which position you are looking at. Jump to the next mismatch with a single keystroke. Everything stays in the browser — nothing leaves your machine.
What I shipped
- Stacked base-call comparison. When two or more traces are resident in the workspace, a new Clone screen panel appears below the consensus row. Each loaded trace occupies one row; columns are aligned by position (clamped to the shortest sequence). Unanimously-agreed columns are coloured teal; any column where at least one trace disagrees is coloured amber with an underline.
- Synchronized cursor. A blue cursor column highlights the focused position across every trace row simultaneously. Moving the cursor instantly scrolls the active chromatogram viewport to that base, so you can inspect the underlying signal for any position of interest without losing context.
- Keyboard-first navigation. Tab into the panel, then press ← / → to step the cursor one column at a time, [ / ] to jump to the previous or next mismatch column, and Home / End to jump to the first or last mismatch in the whole sequence. The keyboard-hint line at the bottom of the panel is always visible as a quick reference.
- Jump buttons. ← Prev mismatch and Next mismatch → buttons give mouse users the same navigation experience. Both are disabled (and visually dimmed) when there are zero mismatches.
- Copy mismatch report. The "Copy report" button writes a tab-separated mismatch table to the clipboard: one header row (Position, then one column per file name) followed by one data row per mismatch column (1-based position, then the base from each trace). Paste it straight into a spreadsheet or lab notebook.
- Live summary line. The panel header shows the trace count, the comparison length in bp, and the exact mismatch count so you know at a glance how many positions need attention.
- Full light + dark theme. All colours use CSS custom properties with matching dark-palette overrides — teal for agreement, amber for mismatches, blue for the cursor — with ≥ 4.5 : 1 measured contrast in both modes.
-
Accessibility throughout.
The panel carries
role="region"with an aria-label; each trace row has an aria-label with the file name; cursor position changes are announced via a live region (aria-live="polite"); the panel receives a visible focus ring on keyboard focus; navigation buttons have descriptive aria-labels.
Architecture
-
src/cloneScreen/stackedViewer.ts— pure TypeScript, zero DOM. ExportscomputeStackedView(sequences)which returns per-columnStackedColumnobjects (position, per-trace bases, allAgree flag, IUPAC consensus base), sortedmismatchIndices, and summary counts. Also exportsnextMismatch,prevMismatch,clampCursor, andbuildMismatchReport(TSV formatter). All computations are client-side; no data leaves the browser. -
src/components/CloneScreenPanel.ts— DOM component.createCloneScreenPanel(onCursorChange)returns a panel element and a state object;renderCloneScreen(elements, sequences, fileNames)paints the ruler + N trace rows + keyboard hint;hideCloneScreen(elements)tears it down. Cursor moves callonCursorChange(position)soTraceViewercan scroll the chromatogram. -
src/components/TraceViewer.ts— addedrefreshCloneScreen()(mirrorsrefreshConsensus()) and a combined helperrefreshMultiTracePanels()that calls both plussyncContigPanel(). All trace-set change events now go through this helper so the clone screen is always in sync. -
src/style.css— 17 new design tokens for the clone screen (light + dark), plus component styles for the panel, ruler, base cells (agree / mismatch / cursor states), navigation buttons, and keyboard hint.prefers-reduced-motionguard strips the base-cell background transition when motion is reduced.
Tests
-
tests/core/cloneScreen.test.ts— 45 exact-value unit tests:-
computeStackedView— edge cases (empty, single sequence); EXACT mismatch indices[1, 3]for the 2-trace case; EXACT mismatch indices[1, 7]for the 3-trace case; per-column base arrays; allAgree/consensusBase; IUPAC resolution (C+T → Y, T+A → W, C-plurality → C); case normalization. -
nextMismatch/prevMismatch— EXACT cursor positions for known mismatch lists including null returns at boundaries. -
clampCursor— negative, over-length, zero-length, and mid-range cases. -
buildMismatchReport— EXACT TSV bytes for a 2-trace and a 3-trace known input; empty string for zero mismatches; 1-based position column verified.
-
-
tests/e2e/clone-screen.e2e.test.ts— 15 Playwright tests: panel hidden with one trace; visible with two; EXACT "0 mismatches" when the same file is loaded twice; EXACT "2 traces" in summary; keyboard focus + ArrowRight → cursor moves to Position 2 → ArrowLeft → Position 1; prev/next buttons visible; copy-report button visible; two trace rows rendered, no third; cursor cell has--cursorclass at position 0 on load; two different AB1 files → mismatch count > 0; keyboard hint visible; role=region + aria-label; bp count in summary.