feat(visualization): add genome browser - #28
Conversation
Reviewer's GuideImplements Phase 6.2 Genome Browser: a reusable, typed genome visualization atop the Phase 6.1 VisualizationContainer and Phase 5 coordinate-search API, including viewport math, region parsing, track rendering, and a demo wired into the /visualization page with comprehensive tests and docs. Sequence diagram for Genome Browser viewport-driven data loadingsequenceDiagram
actor User
participant GenomeBrowser
participant useGenomeBrowser
participant useVisualizationData
participant genome_api
participant Phase5_coordinate_search_API
User->>GenomeBrowser: click Zoom in / enter region
GenomeBrowser->>useGenomeBrowser: zoomIn() / navigateTo(interval)
useGenomeBrowser->>useGenomeBrowser: update viewport state
useGenomeBrowser->>useGenomeBrowser: useDebouncedViewport(viewport, debounceMs)
useGenomeBrowser->>useVisualizationData: loader(interval, signal)
useVisualizationData->>genome_api: fetchIntervalFeatures(interval, signal)
useVisualizationData->>genome_api: fetchVariantFeatures(interval, signal)
genome_api->>Phase5_coordinate_search_API: POST /search/{domain}/coordinate
Phase5_coordinate_search_API-->>genome_api: items[]
genome_api-->>useVisualizationData: GenomicFeature[] / VariantFeature[]
useVisualizationData-->>GenomeBrowser: trackResults updated
GenomeBrowser-->>User: updated axis and SVG tracks
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Warning Review limit reached
Next review available in: 52 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR adds a Genome Browser with genomic types, coordinate parsing, viewport navigation, API adapters, track layout, SVG rendering, browser controls, tests, visualization-page integration, and Phase 6.2 documentation. ChangesGenome Browser
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant GenomeBrowserDemo
participant GenomeBrowser
participant useGenomeBrowser
participant fetchIntervalFeatures
participant CoordinateSearchAPI
GenomeBrowserDemo->>GenomeBrowser: provide viewport and tracks
GenomeBrowser->>useGenomeBrowser: initialize browser state
useGenomeBrowser->>fetchIntervalFeatures: request visible interval
fetchIntervalFeatures->>CoordinateSearchAPI: POST overlapping interval
CoordinateSearchAPI-->>fetchIntervalFeatures: return coordinate records
fetchIntervalFeatures-->>useGenomeBrowser: return normalized features
useGenomeBrowser-->>GenomeBrowser: provide track status and features
GenomeBrowser-->>GenomeBrowserDemo: render controls and tracks
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- BrowserStatus renders an but tests query role="status"; consider adding role="status" (or adjusting tests) so the accessibility semantics and test expectations align.
- useDebouncedViewport relies on window.setTimeout directly; if you ever reuse this hook outside of strictly client-only components, consider guarding against non-browser environments to avoid reference errors during SSR or tests.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- BrowserStatus renders an <output aria-live="polite"> but tests query role="status"; consider adding role="status" (or adjusting tests) so the accessibility semantics and test expectations align.
- useDebouncedViewport relies on window.setTimeout directly; if you ever reuse this hook outside of strictly client-only components, consider guarding against non-browser environments to avoid reference errors during SSR or tests.
## Individual Comments
### Comment 1
<location path="apps/web/src/lib/genome/region.ts" line_range="37-46" />
<code_context>
+const REGION_PATTERN = /^([^:\s]+)\s*:\s*(-?\d+)\s*-\s*(-?\d+)\s*$/i
</code_context>
<issue_to_address>
**issue (bug_risk):** Negative coordinate handling is inconsistent with the documented error codes and pattern semantics.
Because `REGION_PATTERN` only accepts `-?\d+`, inputs with non-numeric coordinates (e.g. `chr1:abc-200000`) are rejected as `malformed` before `coordinateError` runs, while `chr1:-100-200000` yields `negative_start`. However, patterns like `chr1:100--200000` fail the regex and are also marked `malformed`, even though a `negative_end` error code exists. This mismatch makes error codes hard to predict and reason about. Please consider relaxing the regex to accept more general coordinate tokens and performing all numeric/negativity checks in one validation path, so `malformed`, `negative_*`, and `invalid_*` errors are applied consistently.
</issue_to_address>
### Comment 2
<location path="apps/web/src/lib/genome/useGenomeBrowser.ts" line_range="174-180" />
<code_context>
+ setViewport({ ...initialViewport, bounds: initialViewport.bounds })
+ }, [initialViewport])
+
+ const navigateTo = useCallback((interval: GenomicInterval) => {
+ setViewport((current) => ({
+ chromosome: interval.chromosome,
+ start: interval.start,
+ end: interval.end,
+ bounds: current.bounds,
+ }))
+ }, [])
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Navigating to a new chromosome keeps the previous contig bounds, which can incorrectly clamp the viewport.
In `navigateTo`, the new viewport reuses `current.bounds`. If the user switches to a different chromosome/contig, those bounds may not match the new contig (or may be undefined when they should be defined), causing incorrect pan/zoom clamping or making an open-ended contig appear bounded. Consider clearing `bounds` on cross-chromosome navigation, or passing the correct bounds with the interval when contig metadata is available, instead of inheriting the previous viewport’s bounds.
</issue_to_address>
### Comment 3
<location path="apps/web/src/components/genome/GenomeBrowser.tsx" line_range="123-127" />
<code_context>
+ const handleSubmit = useCallback(
+ (event: FormEvent<HTMLFormElement>) => {
+ event.preventDefault()
+ const result = parseGenomeRegion(regionText)
+ if (result.ok) {
+ browser.navigateTo(result.interval)
+ }
+ setRegionText('')
+ },
+ [browser, regionText],
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Clearing the region input even on parse failure can hurt UX and accessibility.
In `BrowserControls.handleSubmit`, `regionText` is cleared on every submit, even when `parseGenomeRegion` fails. This removes the user’s input with no feedback and makes correcting errors harder, particularly for keyboard-only and screen-reader users. Please only clear `regionText` on successful parses and expose `RegionValidationError` (e.g., inline error message or `aria-live`) so users can understand and fix invalid input.
Suggested implementation:
```typescript
function BrowserControls({ browser }: { browser: GenomeBrowserResult }) {
const [regionText, setRegionText] = useState('')
const [regionError, setRegionError] = useState<RegionValidationError | null>(null)
```
```typescript
const handleSubmit = useCallback(
(event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
const result = parseGenomeRegion(regionText)
if (result.ok) {
browser.navigateTo(result.interval)
setRegionText('')
setRegionError(null)
} else {
setRegionError(result.error)
}
},
[browser, regionText],
)
```
1. Ensure `RegionValidationError` is imported in this file (likely from the same module as `parseGenomeRegion`), e.g.:
`import { parseGenomeRegion, RegionValidationError } from '...';`.
2. In the JSX for `BrowserControls`, render an inline error message when `regionError` is non-null, using accessible patterns (e.g. `aria-live="polite"` on the error container and `aria-describedby` on the region input pointing to the error element).
3. Optionally clear `regionError` on input change (`onChange` for the region field) so that the error message disappears as the user edits, while still preserving their existing input.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 14
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/genome/GenomeBrowser.test.tsx`:
- Around line 121-126: Update the test around the genes-lane resolution to
assert “Loading genes...” before calling resolveGenes([]), then assert “No genes
found in region.” after the resolution completes. Keep the existing genesLoader
invocation check if needed, but ensure the assertions verify the
loading-to-empty state transition rather than only confirming the loader was
called.
In `@apps/web/src/components/genome/GenomeBrowser.tsx`:
- Around line 123-127: Update the region submission flow around
parseGenomeRegion so failed parsing preserves the submitted regionText, marks
the input invalid, and renders an accessible validation message; retain
navigation and clearing only for valid intervals. Update the related
invalid-input test to assert the value remains, the invalid state is set, and
the error message is rendered.
- Around line 100-108: Update the reverse-strand arrow geometry in the feature
rendering logic so the triangle is positioned at the feature’s start edge and
remains visible rather than being covered by the rectangle. Adjust the `arrow`
calculation for `forward === false` while preserving the existing forward-strand
rendering and dimensions.
- Line 38: Update the axis SVG in GenomeBrowser to use the same responsive width
behavior as the track SVGs rendered with w-full, removing the min-w-[640px]
constraint while preserving the existing viewBox and height configuration so
genomic tick positions align at all container widths.
In `@apps/web/src/lib/genome/api.test.ts`:
- Around line 109-195: Update requestCoordinateSearch, used by
fetchIntervalFeatures and fetchVariantFeatures, to fetch and combine subsequent
coordinate-search pages while page_size multiplied by the current page remains
below total_count, preserving the existing normalized result behavior. Add
coverage in the coordinate-fetch tests with total_count greater than page_size
and verify later-page items are included.
In `@apps/web/src/lib/genome/api.ts`:
- Line 197: The coordinate and variant loaders currently fetch only the first
page, so update both request flows in apps/web/src/lib/genome/api.ts at lines
197-197 and 210-210 to continue requesting pages until all interval results are
loaded, using the response pagination total_count or equivalent metadata.
Preserve the supplied AbortSignal on every page request and combine all returned
items before returning.
In `@apps/web/src/lib/genome/chromosome.ts`:
- Around line 11-21: Restrict CHROMOSOME_PATTERN and normalizeChromosome to
accept autosomes only from 1 through 22, while canonicalizing both M and MT to
chrMT. Update apps/web/src/lib/genome/chromosome.ts lines 11-21 and add
corresponding expectations in apps/web/src/lib/genome/chromosome.test.ts lines
16-26 for M, while rejecting chr23, chr99, and equivalent unsupported
identifiers.
In `@apps/web/src/lib/genome/geometry.ts`:
- Around line 84-98: Normalize minorPerMajor in computeTicks to a finite integer
before calculating minorStep, ensuring minorStep is an integer of at least one
so generated genomic tick positions remain integral and the loop always
advances. Preserve the existing major/minor tick behavior, and add regression
coverage for a non-aligned start and an infinite divisor.
In `@apps/web/src/lib/genome/region.test.ts`:
- Around line 50-54: Update the non-digit start-coordinate test for
parseGenomeRegion to expect the invalid_start error code returned by
coordinateError instead of malformed. Add a corresponding test for a non-digit
end coordinate and assert invalid_end if that path is not already covered.
In `@apps/web/src/lib/genome/region.ts`:
- Around line 104-105: Validate the parsed start and end coordinates after
converting rawStart and rawEnd to numbers, requiring both start and end to
satisfy Number.isSafeInteger before the region parser returns success. Preserve
the existing invalid-coordinate failure behavior and prevent unsafe or infinite
values from reaching the viewport or API request.
In `@apps/web/src/lib/genome/tracks.ts`:
- Around line 90-95: Update featuresInViewport to require feature.chromosome ===
viewport.chromosome before applying the existing inclusive coordinate-overlap
condition, so features from other chromosomes are excluded. Add a test covering
overlapping numeric coordinates across different chromosomes.
In `@apps/web/src/lib/genome/useGenomeBrowser.ts`:
- Around line 174-180: Update navigateTo so bounds are cleared when
interval.chromosome differs from the current viewport chromosome, rather than
reusing current.bounds. When navigating on the same chromosome and bounds are
available, clamp interval.start and interval.end to those bounds before storing
the viewport; preserve the submitted interval when bounds do not apply.
- Line 148: Refactor useGenomeBrowser so useGenomeTrack is not invoked inside
the tracks.map callback. Move the per-track Hook usage into a stable child
component or aggregate Hook implementation, ensuring Hook call order remains
consistent when tracks are added, removed, or reordered while preserving the
existing trackResults behavior.
In `@docs/visualization/genome-browser.md`:
- Around line 37-43: Add language identifiers to the fenced code blocks in
genome-browser.md: mark the architecture diagram block as text and the
validation commands block as shell, including the corresponding closing fences.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d21886c2-112d-4602-8e57-0d2e2126fd62
📒 Files selected for processing (22)
apps/web/src/app/visualization/GenomeBrowserDemo.tsxapps/web/src/app/visualization/page.tsxapps/web/src/components/genome/GenomeBrowser.test.tsxapps/web/src/components/genome/GenomeBrowser.tsxapps/web/src/lib/genome/api.test.tsapps/web/src/lib/genome/api.tsapps/web/src/lib/genome/chromosome.test.tsapps/web/src/lib/genome/chromosome.tsapps/web/src/lib/genome/geometry.test.tsapps/web/src/lib/genome/geometry.tsapps/web/src/lib/genome/region.test.tsapps/web/src/lib/genome/region.tsapps/web/src/lib/genome/tracks.test.tsapps/web/src/lib/genome/tracks.tsapps/web/src/lib/genome/types.tsapps/web/src/lib/genome/useGenomeBrowser.test.tsxapps/web/src/lib/genome/useGenomeBrowser.tsapps/web/src/lib/genome/viewport.test.tsapps/web/src/lib/genome/viewport.tsdocs/visualization/README.mddocs/visualization/genome-browser.mddocs/visualization/roadmap.md
|
ⓘ Your Qodo trial ends soon. Ask your workspace admin to set up billing to keep reviews running after the trial. Manage billing |
PR Summary by QodoAdd Phase 6.2 Genome Browser visualization (viewport + tracks + API adapter)
AI Description
Diagram
High-Level Assessment
Files changed (22)
|
Code Review by Qodo
1. Hooks called in map
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/genome/GenomeBrowser.tsx`:
- Around line 282-292: Use a single displayed viewport for both AxisSvg and
BrowserTrack so axis ticks and track glyphs remain aligned during pan, zoom, and
region navigation. Update the viewport passed through the GenomeBrowser render
path, preserving debouncing only if stale glyphs are hidden while settling, and
add a navigation test covering the debounce interval.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ffa81d93-b896-4983-b916-46d2ed8bc9e4
📒 Files selected for processing (12)
apps/web/src/components/genome/GenomeBrowser.test.tsxapps/web/src/components/genome/GenomeBrowser.tsxapps/web/src/lib/genome/api.test.tsapps/web/src/lib/genome/api.tsapps/web/src/lib/genome/geometry.test.tsapps/web/src/lib/genome/geometry.tsapps/web/src/lib/genome/region.test.tsapps/web/src/lib/genome/region.tsapps/web/src/lib/genome/tracks.test.tsapps/web/src/lib/genome/tracks.tsapps/web/src/lib/genome/useGenomeBrowser.test.tsxapps/web/src/lib/genome/useGenomeBrowser.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/web/src/lib/genome/region.test.ts
- apps/web/src/lib/genome/tracks.ts
- apps/web/src/lib/genome/api.test.ts
- apps/web/src/lib/genome/useGenomeBrowser.test.tsx
- apps/web/src/lib/genome/geometry.test.ts
- apps/web/src/lib/genome/geometry.ts
- apps/web/src/lib/genome/region.ts
Summary
Implements Phase 6.2 — Genome Browser of the GenomeAI Visualization Platform.
This builds on the Phase 6.1 visualization foundation and introduces the first
real biological visualization in GenomeAI: a reusable genome browser for
viewing genomic regions and coordinate-based features.
Features
Genome Browser
Architecture
API Integration
Uses the existing Phase 5 coordinate-search API:
POST /search/{domain}/coordinateThe browser requests only the currently visible genomic interval.
No backend changes were required.
Testing
Added comprehensive frontend coverage for:
Verification
make setupmake lintmake typecheckmake testpnpm turbo buildResults:
/visualizationroute: builds successfullyDependencies
No new visualization dependencies were introduced.
No:
The implementation uses the existing React/TypeScript/Next.js foundation.
Documentation
Added:
Updated:
Roadmap
Completed
Next
Phase 6.3 — Gene / Transcript Visualization
Future milestones:
Scope
This PR intentionally does not implement:
Those belong to subsequent milestones.
Summary by Sourcery
Introduce an interactive genome browser as the first concrete visualization on top of the Phase 6.1 visualization foundation, wired to the existing coordinate-search API and exposed via the /visualization page.
New Features:
Enhancements:
Documentation:
Tests:
Summary by CodeRabbit