Skip to content

Commit f134599

Browse files
Release v0.1.26-alpha
Add dwell-scaled base widths and toggleable base color annotations for reference overlay plots.
1 parent 91f79e3 commit f134599

File tree

15 files changed

+998
-79
lines changed

15 files changed

+998
-79
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Squiggy Release Notes
22

3+
## v0.1.26-alpha (2026-03-03)
4+
5+
Reference overlay improvements with dwell-scaled base widths and toggleable base color annotations.
6+
7+
### Features
8+
9+
- **Dwell-Scaled Base Widths**: Reference overlay plots can now scale base widths proportional to pore dwell time, making longer-dwelled bases visually wider
10+
- **Toggle Base Colors**: New checkbox to show/hide colored ACGT background patches in reference overlay plots
11+
- **Reference Overlay UI Controls**: Added "Scale by dwell time" and "Show base colors" checkboxes in the Plotting panel for reference overlay mode
12+
13+
### Fixes
14+
15+
- **Reference Overlay Gating**: Gate reference overlay availability on BAM presence only, not on event (mv tag) availability
16+
- **Upfront Session Validation**: Consolidated file validation with clear error dialogs at session load time
17+
318
## v0.1.24-alpha (2026-01-22)
419

520
Pipeline session integration for external workflow support.

package-lock.json

Lines changed: 26 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "squiggy-positron",
33
"displayName": "Squiggy",
44
"description": "Visualize Oxford Nanopore sequencing data (squiggle plots) from POD5 files",
5-
"version": "0.1.24-alpha",
5+
"version": "0.1.26-alpha",
66
"publisher": "rnabioco",
77
"icon": "resources/icon-128.png",
88
"repository": {

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "squiggy-positron"
3-
version = "0.1.24"
3+
version = "0.1.26"
44
description = "Python backend for Squiggy Positron extension - Nanopore squiggle visualization. Not recommended for standalone use."
55
readme = "docs/PYPI_README.md"
66
requires-python = ">=3.12"

squiggy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
>>> show(Div(text=html))
2424
"""
2525

26-
__version__ = "0.1.24"
26+
__version__ = "0.1.26"
2727

2828
# Object-oriented API (NEW - notebook-friendly)
2929
# Core data structures and constants

squiggy/alignment.py

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,29 @@ def extract_alignment_from_bam(bam_path: Path, read_id: str) -> AlignedRead | No
5858
return None
5959

6060

61+
def _is_rna_read(alignment) -> bool:
62+
"""Check if alignment is from an RNA read by inspecting the RG tag.
63+
64+
RNA basecall models include "rna" in the model name (e.g.,
65+
"rna004_130bps_sup@v5.1.0"). The RG tag format is "{run_id}_{model_name}".
66+
67+
Args:
68+
alignment: pysam AlignmentSegment object
69+
70+
Returns:
71+
True if the read was basecalled with an RNA model
72+
"""
73+
if not alignment.has_tag("RG"):
74+
return False
75+
rg_value = alignment.get_tag("RG")
76+
# RG format: {run_id}_{model_name}
77+
if "_" in rg_value:
78+
model_name = rg_value.split("_", 1)[1]
79+
else:
80+
model_name = rg_value
81+
return "rna" in model_name.lower()
82+
83+
6184
def _parse_alignment(alignment) -> AlignedRead | None:
6285
"""Parse a pysam AlignmentSegment into AlignedRead
6386
@@ -92,9 +115,29 @@ def _parse_alignment(alignment) -> AlignedRead | None:
92115
if query_pos is not None and ref_pos is not None:
93116
query_to_ref[query_pos] = ref_pos
94117

118+
# Get trim_start offset (ts tag): number of raw signal samples to skip
119+
# before the basecalled region begins (adapter/polyA trimming).
120+
# Without this, signal indices point into adapter signal instead of bases.
121+
trim_start = 0
122+
if alignment.has_tag("ts"):
123+
trim_start = int(alignment.get_tag("ts"))
124+
125+
# RNA orientation fix:
126+
# - BAM query_sequence is always stored 5'→3' (BAM convention).
127+
# - The mv tag (move table) is in signal order — 3'→5' for direct RNA.
128+
# - For DNA these are the same orientation; for RNA they are opposite.
129+
# - We reverse the query-space lookup so each signal range maps to the
130+
# correct query base. Signal positions remain unchanged.
131+
is_rna = _is_rna_read(alignment)
132+
seq_len = len(sequence)
133+
134+
def query_idx(move_base_idx: int) -> int:
135+
"""Map move-table base index to query-space position."""
136+
return (seq_len - 1 - move_base_idx) if is_rna else move_base_idx
137+
95138
# Convert move table to base annotations
96139
bases = []
97-
signal_pos = 0
140+
signal_pos = trim_start
98141
base_idx = 0
99142

100143
for move_idx, move in enumerate(moves):
@@ -113,17 +156,19 @@ def _parse_alignment(alignment) -> AlignedRead | None:
113156
# No next base found, extend to end of signal
114157
signal_end = signal_pos + ((len(moves) - move_idx) * stride)
115158

159+
qi = query_idx(base_idx)
160+
116161
# Get genomic position using aligned_pairs (handles indels correctly)
117-
genomic_pos = query_to_ref.get(base_idx)
162+
genomic_pos = query_to_ref.get(qi)
118163

119164
# Get quality score
120165
quality = None
121166
if alignment.query_qualities is not None:
122-
quality = alignment.query_qualities[base_idx]
167+
quality = alignment.query_qualities[qi]
123168

124169
base = BaseAnnotation(
125-
base=sequence[base_idx],
126-
position=base_idx,
170+
base=sequence[qi],
171+
position=qi,
127172
signal_start=signal_pos,
128173
signal_end=signal_end,
129174
genomic_pos=genomic_pos,

0 commit comments

Comments
 (0)