Skip to content

Stage B/C: OpenMS-Insight viewers for FLASHDeconv & FLASHTnT#91

Open
t0mdavid-m wants to merge 10 commits into
developfrom
claude/flashapp-openms-visualizations-LVv66
Open

Stage B/C: OpenMS-Insight viewers for FLASHDeconv & FLASHTnT#91
t0mdavid-m wants to merge 10 commits into
developfrom
claude/flashapp-openms-visualizations-LVv66

Conversation

@t0mdavid-m
Copy link
Copy Markdown
Member

@t0mdavid-m t0mdavid-m commented Jun 1, 2026

Summary

Adds new OpenMS-Insight-based viewers for FLASHDeconv (Stage B) and FLASHTnT (Stage C) workflows, replacing the legacy bespoke flash_viewer_grid Vue component. Both viewers use reusable OpenMS-Insight components (Table, LinePlot, Heatmap, SequenceView, DensityPlot) with shared StateManager per experiment panel to enable cross-component interactivity while preventing state leakage between side-by-side panels.

Key Changes

New Viewers

  • content/FLASHDeconv/FLASHDeconvViewerOI.py (Stage B): Renders FLASHDeconv results using OpenMS-Insight components. Wires scan/mass selection across Scan Table → Mass Table → Deconvolved/Combined Spectrum → SequenceView → 3D S/N Plot via shared StateManager keyed by svc_state_deconv_<experiment_id>.
  • content/FLASHTnT/FLASHTnTViewerOI.py (Stage C): Renders FLASHTnT (top-down identification) results. Adds proteoform→scan resolution via build_proteoform_scan_frame() so OpenMS-Insight value-filters work correctly. Implements tagger overlay: Tag Table selection resolves to mass list, LinePlot highlights matching peaks within ±1e-5 Da tolerance.

Long-Format Frame Producers

  • src/parse/deconv.py: Added _explode_long_by_position() helper and producers:

    • deconv_spectrum_long() — one row per deconvolved peak
    • anno_spectrum_long() — one row per annotated/raw peak
    • combined_spectrum_long() — deconv peaks with signal-membership flag
    • mass_table_long() — one row per mass with charge/score columns

    These replace array-per-scan frames for OpenMS-Insight components, which filter by column VALUE (e.g., filters={'scanIndex': 'index'}) rather than row INDEX. Handles ragged scans (spectrum longer than per-mass arrays) by padding with nulls, matching legacy Vue expansion semantics.

Routing & Settings

  • content/FLASHDeconv/FLASHDeconvViewer.py & content/FLASHTnT/FLASHTnTViewer.py: Added render_panel() dispatcher that routes to OpenMS-Insight viewer when settings.use_openms_insight_viewer is True (default), else legacy grid.
  • settings.json: Added "use_openms_insight_viewer": true flag to enable new viewers by default.

Scan Resolution (TnT-specific)

  • src/render/scan_resolution.py: Added build_proteoform_scan_frame() (additive, reproduces legacy build_proteoform_scan_map as a Polars frame). Surfaces proteoform_index → (scan, deconv_index) as columns so spectrum/sequence frames can be joined and value-filtered by proteoform selection.

Testing

  • tests/test_deconv_long_format.py: Unit tests for long-format producers validating row-count fidelity, index-filter parity, ragged-scan padding, and id-column alignment without requiring Streamlit or pyopenms.

Docker & Dependencies

  • Dockerfile & Dockerfile.arm: Updated to build OpenMS-Insight Vue bundle from source (migration branch) in a separate oi-build stage, since the pinned PyPI release does not yet include new FLASHApp components.
  • requirements.txt: Added openms-insight>=0.1.11 dependency.

Implementation Details

  • StateManager per panel: Each experiment panel gets a distinct session key (e.g., `svc_state_deconv_<experiment_id>

https://claude.ai/code/session_01VM8hWcakFabPeAAx1UjGUE

Summary by CodeRabbit

  • New Features

    • Added OpenMS‑Insight visualization mode across FLASHDeconv, FLASHTnT, and FLASHQuant for richer interactive plots (tables, spectra, heatmaps, sequence view, density/FDR) with per‑panel isolated state and optional overlays.
    • Added a settings toggle to switch between the new viewer and the classic view.
  • Tests

    • Added tests for long‑format data producers to validate alignment, padding, and signal/axis behavior for the new visualizations.

claude added 4 commits May 31, 2026 23:02
Stage B (FLASHDeconv) + Stage D (FLASHQuant) of the migration off the
bespoke flash_viewer_grid onto the reusable OpenMS-Insight library.

Parse layer (src/parse/deconv.py): add long-format producers
(deconv_spectrum_long, anno_spectrum_long, combined_spectrum_long with
is_signal, mass_table_long with index+mass_id) alongside the existing
array-format frames (additive, legacy path untouched), so components can
filter by column value instead of row index.

FLASHDeconv: new FLASHDeconvViewerOI builds the scan/mass tables, deconv/
annotated/combined LinePlots, Scatter3D precursor-signal plot, 4 heatmaps,
DensityPlot and SequenceView, wired through one StateManager per experiment
panel (distinct session_key => side-by-side selection isolation) with full
[experiment][row][col] layout parity. Routed behind settings flag
use_openms_insight_viewer (default true); legacy render_grid retained.

FLASHQuant: render the feature-group view via FeatureView (standalone),
no data transformation required.

requirements: add openms-insight>=0.1.11.
Stage C of the migration off flash_viewer_grid. New FLASHTnTViewerOI
mirrors the FLASHDeconv viewer: protein/tag Tables, SequenceView (coverage
+ fixed mods), the combined-spectrum LinePlot with tagger overlay (2nd
series + signal-peak markers + sequence-tag highlight), TnT-mode
DensityPlot, and heatmaps — wired through one StateManager per experiment
panel (distinct session_key => side-by-side isolation) with full
[experiment][row][col] layout parity. Routed behind the same
use_openms_insight_viewer flag; legacy render_grid retained.

Proteoform->scan resolution: add build_proteoform_scan_frame (additive,
legacy build_proteoform_scan_map untouched) so proteoform selections
resolve to deconv scans and components value-filter by proteoform_index,
reproducing the old PyArrow pushdown. Tag selection resolves tag m/z
values into tagMasses for the spectrum overlay.

InternalFragmentMap remains disabled (as in the legacy path); enabling it
needs the per-proteoform sequence_data store extended with internal-
fragment arrays.
deconv.py (_explode_long_by_position): the long-format explode kept the
full per-scan value lists on every exploded row and then gathered the
scalar — O(rows x max_len) memory, which OOMs on real spectra (an 865k-row
annotated frame with multi-thousand-length lists reached tens of GB).
Pad each value list to the per-scan max length with nulls and zip-explode
the id column and all value lists together, staying O(output rows) with
identical output (parity tests unchanged).

FLASHTnTViewerOI (_build_sequence_frame): sequence_data is a pickle-backed
dict keyed by proteoform_index, not a tabular frame. Loading it as a
LazyFrame raised AttributeError and left SequenceView blank for every TnT
dataset. Load the raw dict and iterate it, slicing the displayed proteoform
substring and its coverage together so they stay aligned.
The new FLASHApp viewers render via OpenMS-Insight, whose components are not
yet in the published PyPI release. Add an oi-build node stage (x86 + arm)
that clones the OpenMS-Insight branch, builds its Vue bundle, and stages it
at openms_insight/js-component/dist; then install the package from source in
the run-app stage so the bundle is baked into site-packages where the runtime
bridge loads it. openms-insight is stripped from requirements.txt so the PyPI
release is not pulled instead. Repo/branch are overridable via the
OPENMS_INSIGHT_REPO/OPENMS_INSIGHT_BRANCH build args (default: the migration
branch). CI builds the image unchanged via the build-arg defaults.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Warning

Review limit reached

@t0mdavid-m, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 7 minutes and 36 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 75346665-0176-474f-ab04-b449b417b6a9

📥 Commits

Reviewing files that changed from the base of the PR and between 85d20d4 and 6553258.

📒 Files selected for processing (6)
  • content/FLASHDeconv/FLASHDeconvLayoutManager.py
  • content/FLASHDeconv/FLASHDeconvViewerOI.py
  • content/FLASHDeconv/deconv_sequence.py
  • content/FLASHTnT/FLASHTnTViewerOI.py
  • src/parse/deconv.py
  • tests/test_deconv_long_format.py
📝 Walkthrough

Walkthrough

This PR integrates OpenMS-Insight into FLASHApp: adds Docker oi-build stages, produces long-format per-peak/mass frames, adds viewer routing flags, implements OpenMS-Insight viewers for FLASHDeconv and FLASHTnT (and switches FLASHQuant to FeatureView), and adds tests and config updates.

Changes

OpenMS-Insight Viewer Integration

Layer / File(s) Summary
Docker multi-stage builds for OpenMS-Insight components
Dockerfile, Dockerfile.arm
Adds oi-build stage that clones/builds OpenMS-Insight frontend and stages dist into openms_insight/js-component/dist; runtime stages copy and pip install the built package into the streamlit-env conda environment. Both Dockerfiles filter pyopenms and openms-insight out of requirements.txt before installing other dependencies.
Dependency and configuration updates
requirements.txt, settings.json
Adds openms-insight>=0.1.11 and a top-level use_openms_insight_viewer boolean (true).
Long-format data frame builders
src/parse/deconv.py
Adds LONG_ROW_GROUP_SIZE, _explode_long_by_position, and long-frame producers: deconv_spectrum_long, anno_spectrum_long, combined_spectrum_long (with is_signal), and mass_table_long. parseDeconv now persists these long frames.
Proteoform→scan mapping frame
src/render/scan_resolution.py
Adds Polars import and build_proteoform_scan_frame plus _to_pandas helper to produce a Polars frame mapping proteoform_index→(scan,deconv_index) for viewer wiring.
Tests for long-format builders
tests/test_deconv_long_format.py
New tests stub pyopenms, validate long-format producers for schema/rowcounts, index-filter parity, ragged padding (None), is_signal correctness, and mass/deconv join alignment.
FLASHDeconv viewer routing
content/FLASHDeconv/FLASHDeconvViewer.py
Adds _use_oi_viewer() and render_panel(...) to lazily dispatch per-experiment panels to OpenMS-Insight render_experiment_panel with fallback to legacy render_grid(...); updates all render sites to call render_panel(...).
FLASHDeconv OpenMS-Insight implementation
content/FLASHDeconv/FLASHDeconvViewerOI.py
New OI-based viewer with component builders (Heatmap, Table, LinePlot, SequenceView, Scatter3D, DensityPlot), per-experiment cache dirs, component registry, combined-spectrum concatenation, and per-panel StateManager isolation.
FLASHTnT viewer routing
content/FLASHTnT/FLASHTnTViewer.py
Adds _use_oi_viewer() and render_panel(...) routing; rewires side-by-side, single, and multi-experiment render sites to use render_panel(...), with second-column spinner.
FLASHTnT OpenMS-Insight implementation
content/FLASHTnT/FLASHTnTViewerOI.py
New OI-based FLASHTnT viewer: proteoform mapping, stamping proteoform_index onto spectra, tag→tagMasses resolution for overlays, SequenceView construction (coverage/fixed-mods), combined-spectrum builder, id-FDR density plotting, heatmap variants, and per-panel StateManager isolation.
FLASHQuant viewer switch to FeatureView
content/FLASHQuant/FLASHQuantViewer.py
Switches to openms_insight.FeatureView, loads per-experiment quant_dfs, and renders via feature_view with experiment-scoped cache id/path; legacy rendering remains commented out.

Poem

A bundle of Vue builds in Docker's new stage,
Long-format frames dance on the data-frame page,
Three viewers now speak the Insight tongue,
While legacy grids hum their songs (not yet sung).
With proteoforms tagged and spectra combined,
FLASHApp's visuals leap — a rabbit's hop refined. 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding OpenMS-Insight viewers for FLASHDeconv (Stage B) and FLASHTnT (Stage C), which is the primary objective of this PR across multiple file changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/flashapp-openms-visualizations-LVv66

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
content/FLASHTnT/FLASHTnTViewerOI.py (1)

195-230: ⚡ Quick win

Avoid collecting the full tag table just to resolve one selected tag.

_resolve_tag_masses() only needs the currently selected TagIndex, but _tag_mass_lookup() rebuilds a full lookup dict on every rerun. On large tag_dfs, every panel interaction pays that full collect again.

♻️ Proposed refactor
-def _tag_mass_lookup(file_manager, experiment_id: str) -> dict:
-    """Map ``TagIndex`` -> list[float] of the tag's masses (parsed from the
-    comma-joined ``mzs`` string with its trailing comma). Used to resolve a tag
-    selection into the mass list the combined-spectrum tagger overlay matches."""
-    tags = _lazy(file_manager, experiment_id, "tag_dfs")
-    if tags is None:
-        return {}
-    df = (
-        tags.select(["TagIndex", "mzs"])
-        .with_columns(
-            pl.col("mzs")
-            .str.strip_chars(",")
-            .str.split(",")
-            .list.eval(pl.element().cast(pl.Float64, strict=False))
-            .alias("tag_masses")
-        )
-        .collect()
-    )
-    return {
-        int(ti): [m for m in masses if m is not None]
-        for ti, masses in zip(df["TagIndex"], df["tag_masses"].to_list())
-    }
-
-
 def _resolve_tag_masses(file_manager, experiment_id: str, state_manager) -> None:
     """Resolve the selected ``tagData`` (a ``TagIndex``) to its list of masses and
     publish under ``tagMasses`` so the combined-spectrum LinePlot tagger overlay
     can read it. Clears ``tagMasses`` when no tag is selected."""
     tag_index = state_manager.get_selection(TAG_KEY)
     if tag_index is None:
         state_manager.clear_selection(TAG_MASSES_KEY)
         return
-    lookup = _tag_mass_lookup(file_manager, experiment_id)
-    masses = lookup.get(int(tag_index))
+
+    tags = _lazy(file_manager, experiment_id, "tag_dfs")
+    if tags is None:
+        state_manager.clear_selection(TAG_MASSES_KEY)
+        return
+
+    selected = (
+        tags.filter(pl.col("TagIndex") == int(tag_index))
+        .select(
+            pl.col("mzs")
+            .str.strip_chars(",")
+            .str.split(",")
+            .list.eval(pl.element().cast(pl.Float64, strict=False))
+            .alias("tag_masses")
+        )
+        .collect()
+    )
+    masses = selected["tag_masses"][0] if selected.height else None
+
     if masses:
         state_manager.set_selection(TAG_MASSES_KEY, list(masses))
     else:
         state_manager.clear_selection(TAG_MASSES_KEY)
🤖 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 `@content/FLASHDeconv/FLASHDeconvViewer.py`:
- Around line 14-34: The page introduces viewer-selection helpers
(_use_oi_viewer and render_panel) before calling page_setup(), violating the
content/**/*.py contract; to fix, ensure page_setup() is invoked at the very top
of this module before any session-state dependent helpers or move the helper
functions (_use_oi_viewer and render_panel) into a non-page module so they no
longer execute prior to page initialization; update the file so page_setup()
runs first (above _use_oi_viewer) or relocate the functions accordingly and
adjust imports where render_panel or _use_oi_viewer are used.

In `@content/FLASHDeconv/FLASHDeconvViewerOI.py`:
- Around line 343-345: The code currently logs a warning whenever a builder
returns None for a component (the block checking "if component is None:
st.warning(f\"No data for '{comp_name}'.\")"), which produces noisy warnings for
optional-but-absent frames; change this to silently skip by removing the
st.warning call and just continue when component is None, and instead add a
single early validation where the layout is parsed (using the layout/component
registry you already use) to warn only if the layout references an
unknown/unsupported comp_name (not when a builder returns None for an optional
frame). Ensure you update FLASHDeconvViewerOI.py to reference the same comp_name
and component-building registry so the silent-skip behavior is applied in the
current loop and the explicit warning happens only during layout validation.

In `@content/FLASHTnT/FLASHTnTViewer.py`:
- Around line 9-11: The module currently imports render_experiment_panel at
import time which can break the whole page; modify FLASHTnTViewer.py to
lazy-load it inside the _use_oi_viewer() branch: remove the top-level import of
render_experiment_panel and, within the _use_oi_viewer() conditional, attempt to
import render_experiment_panel from content.FLASHTnT.FLASHTnTViewerOI inside a
try/except, falling back to calling render_grid() if the import (or subsequent
use) raises an exception; ensure you reference render_experiment_panel,
_use_oi_viewer, and render_grid so callers remain unchanged and log or handle
the exception appropriately in the except block.
🪄 Autofix (Beta)

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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c13bd654-0f00-4559-be76-ec387cf19082

📥 Commits

Reviewing files that changed from the base of the PR and between a89427d and 62c6b00.

📒 Files selected for processing (12)
  • Dockerfile
  • Dockerfile.arm
  • content/FLASHDeconv/FLASHDeconvViewer.py
  • content/FLASHDeconv/FLASHDeconvViewerOI.py
  • content/FLASHQuant/FLASHQuantViewer.py
  • content/FLASHTnT/FLASHTnTViewer.py
  • content/FLASHTnT/FLASHTnTViewerOI.py
  • requirements.txt
  • settings.json
  • src/parse/deconv.py
  • src/render/scan_resolution.py
  • tests/test_deconv_long_format.py

Comment thread content/FLASHDeconv/FLASHDeconvViewer.py Outdated
Comment thread content/FLASHDeconv/FLASHDeconvViewerOI.py
Comment thread content/FLASHTnT/FLASHTnTViewer.py Outdated
claude added 6 commits June 1, 2026 10:12
From the CodeRabbit review on #91:
- Lazy-import the OpenMS-Insight viewer inside render_panel (FLASHDeconv and
  FLASHTnT) with a try/except fallback to the legacy grid, so an import-time
  failure of the OI stack no longer breaks the whole page before the
  use_openms_insight_viewer fallback can engage.
- FLASHDeconvViewerOI: skip absent optional components silently instead of
  emitting a warning on every rerun (a builder returns None for legitimately
  absent optional frames).
- FLASHTnTViewerOI: resolve the selected tag's masses by filtering tag_dfs on
  the chosen TagIndex instead of collecting the whole table into a lookup dict
  on every rerun (preserves trailing-comma parsing and null-dropping).

Not applied: the suggestion to move page_setup() above the new helper
definitions - those are plain function defs that do not execute at import, and
page_setup()'s call site is unchanged from the file's existing structure (which
already defines DEFAULT_LAYOUT and other helpers before page_setup()).
Address feature-parity gaps in the OpenMS-Insight viewers vs the legacy grid:

- Tables (protein/tag in TnT, scan/mass in Deconv): pass curated
  column_definitions mirroring the legacy Tabulator*.vue configs (human-
  readable titles, only the relevant fields, 2-decimal formatting), instead
  of dumping every raw frame column with auto-generated names.

- Sequence View "No data": _build_sequence_frame expected the pickled-dict
  sequence_data format (stale example cache), but the live parseTnT writes
  it as parquet (build_table -> parquet_sink). Handle BOTH: parquet frame
  (LazyFrame/DataFrame) and legacy dict, building the same coverage/fixed-mod
  frame either way.

- Augmented Deconvolved Spectrum: _resolve_tag_masses now publishes the tag
  walk {masses, residues} (residues = reversed TagSequence aligned to the
  stored-order mass gaps, matching the legacy reversed-index rule) so the OI
  LinePlot renders the residue walk + auto-zoom, not just peak highlights.
Clicking a covered amino acid in the Sequence View now filters the Tag
Table to the tags covering that residue, matching the legacy cross-link.

- _build_sequence_frame emits a sequence_offset column so the SequenceView
  can report protein-absolute residue positions even when the displayed
  sequence is sliced to the proteoform substring (offset is 0 / unchanged
  for the bundled example proteoforms).
- SequenceView interactivity gains {selectedAApos: "residue_position"} (keeps
  the peak_id mapping); the Tag Table gains
  range_filters={selectedAApos: ("StartPos", "EndPos")} alongside the
  existing proteoform filter, so it shows tags with StartPos <= pos <= EndPos.
…sets

Parse (src/parse/deconv.py): preserve per-deconv-peak signal arrays
(signal_mzs/charges/intensities on combined_spectrum_long + deconv_spectrum_long)
for the charge-state drill-down, and add Scan/PrecursorScan/PrecursorMass/
MonoMass to threedim_SN_plot for the 3D precursor-signal lookup.

FLASHTnT viewer: protein/tag tables use dashNegativeOne (-1 -> "-"); protein
table reproduces the default-ON "Best per spectrum" collapse (max Score per
Scan) with a toggle; SequenceView now renders the FULL protein with truncation
(proteoform_start/end + -2 sentinels), ambiguous mod_ranges, Precursor/Proteoform
mass header, and tag-span highlight; _resolve_tag_masses publishes nTerminal +
the tag span; combined spectrum gets the signal drill-down columns; dependent
selections (tag/tagMasses/AApos/tagSpan/mass) clear on proteoform change.

FLASHDeconv viewer: scan/mass tables use the plain 4-decimal "fixed" formatter
(no thousands grouping); Scatter3D wired for the precursor-signal lookup; deconv
spectrum gets the drill-down columns; submitted-sequence SequenceView bakes the
selected fixed C/M mods into the sequence (correct fragment masses), enables the
variable-mod menu, consumes the custom sequence (sequence_out), and emits the
mass header; massIndex resets on scan change.
…ecursor

Re-audit follow-ups:
- Tables: drop precision from the dashNegativeOne columns (ProteoformMass,
  ProteoformLevelQvalue, Nmass, Cmass) so they show the RAW value like legacy
  (precision-4 was destroying tiny Q-values); keep the -1 -> "-" sentinel.
- TnT tag walk: publish selectedAA (= selectedAApos - StartPos when the
  selected residue is within the tag) so the selected-residue gold highlight
  renders; drop zero-valued tag masses (legacy filters != 0); silently skip
  absent components instead of st.warning (match Deconv).
- Deconv Scatter3D: pass the precursor-lookup columns only when all four are
  present in the frame, so stale 4-column threedim_SN_plot caches no longer
  crash the panel (ValueError) and degrade to the legacy per-scan view.
- Deconv SequenceView: stop emitting computed_mass (it forced the TnT branch,
  mislabeled the header "Proteoform", and disabled the variable-mod menu);
  emit the observed precursor_mass from the selected scan so the "Precursor"
  header renders and variable mods stay enabled.
- Remove dead code (_data_path, unused imports) and drop the inert
  internal_fragment_map from the Deconv selectable layout (parity with TnT).
The LinePlot tag walk receives residues already REVERSED to align with the
stored mass order, so the tag-relative selectedAA (an N->C index) always maps
to the mirrored walk gap. Publish nTerminal=False on the tagMasses walk so the
LinePlot mirrors it -- geometrically correct for both N- and C-anchored tags
and matching the legacy behavior (whose nTerminal was effectively always false).
Driving it off Nmass==-1 misplaced the gold highlight for C-anchored tags.

The SequenceView tag-span still carries the real nTerminal (Nmass==-1) for its
own orientation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants