Skip to content

Releases: seqeralabs/nf-metro

1.1.0 - track_gap, packed grid cells, playground upgrades

Choose a tag to compare

@pinin4fjords pinin4fjords released this 01 Jul 10:23
baf6a23

A routing-focused follow-up to 1.0.0's rebuild, plus a new spacing control and several playground quality-of-life fixes. Existing .mmd files render with no changes.

track_gap directive and --track-gap flag

%%metro track_gap: <pixels> sets the visual gap between adjacent line strokes in a bundle - the empty space between their edges, not their centres. The default is 1 px; set it to 0 to bring strokes flush against each other, or raise it (up to 3 px) for more breathing room between co-running lines.

Packing sections into a shared grid cell

Hand-placed sections (%%metro grid: <section> | <col>,<row>) can now share a cell instead of getting one section per cell:

%%metro grid: gatk, variant_calling | 1,0
%%metro grid: realign, reporting     | 1,1

The named sections pack side-by-side along the flow axis, and the cell sizes to fit its members - so a short+long pair in one row lines up top-to-bottom with a long+short pair packed into the same column below it. This keeps folded pipelines compact and frees the cleared corner for the legend. Each member keeps its own direction, ports, and internal layout; only their placement is shared. Stacked single-row sections sharing a rowspan band now distribute across the full band instead of leaving the bottom rows empty.

Playground improvements

  • Logo upload. The playground runs entirely in-browser via Pyodide, which has no access to the local filesystem, so a %%metro logo: file path pasted in from an existing map could never resolve there. A new "+ Logo" button reads a chosen image client-side and writes it into the directive as a data: URI.
  • Line gap is now a main toolbar control (next to Centre ports) instead of buried in Advanced options, wired to the new track_gap directive.
  • The build's commit SHA is shown next to Report a bug and pre-filled into the bug report, so reports carry the exact build they came from.
  • Fixed a service-worker caching bug where returning visitors kept running a stale build after every deploy, because the dev wheel's filename never changed between builds.

Gallery

The nf-core pipelines page now lists an nf-core/seqinspector render, showcasing a %%metro grid: stack of two single-row sections beside a rowspan-2 section.

Notable fixes

A long tail of routing and rendering fixes, mostly in fold, bypass, and convergence handling:

  • Fold-back routing: serpentine multi-line folds under direction: RL render correctly, fold reversal propagates through peel-off junctions, and the TB-exit-to-return-row connector no longer kinks.
  • Bypass routing: same-row bypasses route around (rather than through) a packed cell-mate or an intervening section, and clear exit rows run straight instead of kinking.
  • Convergence sinks: entry lanes order by feeder approach direction, and interchange labels clear their connector bridge and the enlarged end-knob.
  • Sectionless graphs: skip-lines with no subgraph now detour around non-consumer markers instead of breezing through them.
  • 2-way fans with an internal source now centre on their equal siblings.
  • --mode baked output now selects the correct logo variant and pins color-scheme so raster exports aren't theme-dependent.
  • --embed-font output falls back to a generic font family after Inter.

1.0.0 - Routing engine rebuilt, all four flow directions, stable API

Choose a tag to compare

@pinin4fjords pinin4fjords released this 30 Jun 10:54
3744a21

2026-06-30 · GitHub release · Diff

Major milestone release and first release under the seqeralabs GitHub organisation. 300+ commits since 0.7.2. The .mmd input format is backward compatible - existing files render without edits (many will look better thanks to the routing work below).

All four flow directions

nf-metro now supports all four Mermaid graph directions: LR, RL, TB, and BT. Previously only LR and TB were supported; RL and BT are their respective mirrors, with full routing vocabulary for fold sections, bypass-V, convergence sinks, and perpendicular entries.

Routing engine

The majority of the code change in this release is engine work, and most of it is structural rather than a list of isolated fixes. Three large programs ran across the cycle, then a focused fold-routing sprint closed it out.

Centreline-builder unification. Edge routing was consolidated onto a single centreline builder that owns corner anchoring and tapering for every route family - entry-wrap, perpendicular-exit up-and-over, U-shaped bypass, TOP-entry staircase, fan L-shapes, and inter-section bundles. What used to be scattered per-case path construction now flows through one declarative inter-section dispatch table and one builder, so curves are correct by construction rather than patched after the fact (#791-#814, plus the #845 symbolic gap-slot model).

Direction-agnostic layout (AxisFrame). Layout heuristics that were written against hardcoded "TB" string checks were migrated to an AxisFrame abstraction with lane accessors and a single offset-regime applier, and the core handlers adopted a Direction enum. This is what made the new RL and BT directions tractable: every routing handler now operates on one canonical orientation, and the per-direction special cases were retired. The LR/TB rotation model was extended so RL and BT are derived by mirroring at the seam-classifier layer (#918-#922, #1029-#1049).

Robustness hardening. A gate-arm triage pass gave every un-exercised branch in the layout and routing modules a verdict - reachable (with a new fixture), defensive, or a filed bug - and the inter-phase state shared between layout stages was formalised into an explicit protocol. A guard registry with a cost audit and golden baseline now reds CI the moment a layout regresses (#687, #673, #921).

Fold-threshold routing. Folded sections (where a line wraps across a column boundary) got the most targeted-fix work, late in the cycle. The keystone: fold-back routes that cannot be drawn cleanly now raise a FoldThresholdError and are treated as an authoring constraint rather than emitting broken geometry (#1088). On top of that gate:

  • Perpendicular-entry fans slot onto parallel approach channels by feeder order instead of overlapping (#1144).
  • Folded LEFT-exit staircase bundle ordering and concentric nesting (#1143, #1161).
  • Folded bbox bottom aligns to the target section's settled content bottom, not its live bounding box, preventing balance drift on tall fold targets (#1162).
  • Tight 1-row folds no longer leave a visible run-out stub on the bypass-V (#1177).
  • Same-side cul-de-sac entry re-anchors the interior-consumer port instead of routing around it, removing a whole class of U-route generation (#1182).
  • Opposing-line fold-backs under fold compression raise FoldThresholdError rather than tangling (#1187).
  • Convergence-sink ports no longer face the wrong section after fold relocation (#1167).
  • Disconnected components renormalise grid rows after stacking, preventing section overlap (#1164).
  • Corridor-fed single-line sections re-centre instead of drifting off-trunk (#1173); same-column RIGHT-entry corridor descent no longer jogs before dropping (#1178).
  • TB-section bypass-V seats on the line's lane-side grid column, not a fixed offset (#1163); the post-bypass settling pass runs in both validate modes (#1171); convergence-split placement is deterministic across runs (#1168).

Spacing and symmetry.

  • Symmetric 2-way diamonds compact to half pitch through the grid-snap, halving wasted horizontal space (#1076).
  • Upright (TB/BT) label and icon spacing derives from the AxisFrame rather than hardcoded "TB" checks (#1043).
  • Light theme line width corrected to match OFFSET_STEP (4 px -> 3 px) (#1073).

CSS offset-path animations

Ball animations switch from SMIL <animateMotion> to CSS offset-path. SMIL is disabled in some host environments and causes SVG-in-HTML embedding issues; offset-path works reliably across all modern browsers.

Light and dark as independent axes

Brand (nfcore, seqera) and display mode (light, dark) are now independent. A single rendered SVG carries both palettes via CSS light-dark() and adapts to the viewer's color-scheme automatically. Logo assets also adapt per color-scheme with no extra directives required.

Render API and render-many

A stable Python API:

from nf_metro.api import render_string, RenderConfig

svg = render_string(
    graph_text,
    theme="nfcore",
    mode="dark",
    config=RenderConfig(embed_font=True, responsive=True),
)

render_string is the one-call entry point. RenderConfig is a typed dataclass bundling the render-side output options (font embedding, responsive viewBox, class prefixes, color-scheme handling) for callers who prefer a config object over keyword arguments. The render-many CLI command batch-renders a JSON manifest of jobs in a single process, amortising interpreter and import startup across the whole corpus.

Embedded data manifest

Every render now carries a machine-readable JSON manifest - station visual centres, pill dimensions, and line metadata - factored into a dependency-free nf_metro.manifest package and documented as a standard. This is what lets nf-metro serve accept a previously rendered SVG as input, and it gives host systems a stable way to map screen coordinates back to graph elements (#623).

Font portability

Renders can embed the Inter font or convert text to vector paths, so maps display identically whether or not the host has the font installed - important for SVGs that travel into PDFs, slides, and other documents (#839).

Live workflow visualisation (nf-metro serve)

nf-metro serve connects to a running Nextflow workflow and renders a live metro map in the browser, updating as processes progress. New in this release:

  • SVG input - accepts a previously rendered SVG (with embedded manifest) instead of a .mmd source.
  • auto_process mapping - %%metro auto_process: true and process_scope: collapse a Nextflow DAG into a lean process-level map automatically.

Interactive browser playground

A browser-based editor powered by Pyodide runs the full Python engine client-side - edit .mmd source, see the map update live. Features: example dropdown, Nextflow DAG import, graphical topology editing, zoom controls, brand/mode toggles, gzip-compressed share URLs, and a service-worker cache so the Pyodide runtime loads instantly after the first visit.

New documentation site

Rebuilt in Astro / Starlight with a Seqera-branded theme, replacing MkDocs-Material. Metro maps render live at build time; the <Metro> component presents source, CLI command, and rendered map as a three-panel toggle. Versioned docs with an in-page version switcher, Pagefind full-text search, ZoomableSVG lightbox, color-chip swatches in code blocks, and link validation in CI.

CI

  • 3-way parallel test split with pytest-split to cut CI wall-clock time.
  • Python 3.10 dropped (EOL); minimum is now Python 3.11.
  • All GitHub Actions refs pinned to full commit SHAs (seqeralabs org policy).
  • Incremental render-diff: PR previews only re-render changed fixtures.

0.7.2 - rowspan bbox shrink fix

Choose a tag to compare

@pinin4fjords pinin4fjords released this 18 May 11:13
d923696

Patch release fixing a layout regression visible in v0.7.1.

_fan_source_inputs_upward (Stage 6.2) lifts source-input chains above the trunk to fill empty top space, but the bbox bottom shrink (Stage 6.13) couldn't then trim the box: its row-mate predicate counted any Y-overlapping section as a mate, which made the bug self-protecting. An LR/RL section that declares grid_row_span > 1 but whose content fits in one row had its pre-shrink bbox extend into the claimed-but-unfilled lower rows, Y-overlap a row-r+1 neighbour, and the neighbour then pinned the bottom -- blocking the very shrink that would remove the overlap.

Most visible on the nf-core/differentialabundance metro map, where section 1 (data_prep, rowspan=2) ended mid-air between row 0 and row 1.

The row-mate predicate now splits by direction: TB sections keep the Y-overlap check (section placement grows TB bbox_h by section_y_gap so fold-style TB sections visually span into their row-r+1 target); LR/RL sections match on the starting grid row only, with the other section's rowspan respected. The grid-row reservation from rowspan>1 is preserved -- auto-placed sections in the same column still skip the claimed rows; only bbox sizing is corrected.

Three of 54 gallery fixtures change visually (pure bbox shrinks, no station/edge movement):

  • differentialabundance: data_prep bbox_h 508.8 -> 392.0
  • differentialabundance_default: data_prep bbox_h 508.8 -> 392.0
  • genomeassembly_staggered: scaffolding bbox_h 196.3 -> 188.2

Other 51 fixtures are byte-identical.

#382

Full Changelog: 0.7.1...0.7.2

0.7.1 - bypass row-gap fix

Choose a tag to compare

@pinin4fjords pinin4fjords released this 17 May 20:11
a5a457b

Patch release fixing a layout regression in v0.7.0.

Cross-column bypass routes that descend below intervening sections could land in the inter-row gap close enough to the next row's section header that the stacked-line bundle visually crowded the badge. Most visible on the nf-core/differentialabundance metro map, where the limma -> reporting bypass sat ~6px above the Plots header.

Section placement is now bypass-aware: the row gap is sized against the deepest predicted bypass route, not just bbox bottoms. Only the gallery's differentialabundance / differentialabundance_default and the topology complex_multipath plus the da_pipeline and genomeassembly_organellar fixtures change visually (each gains 25px height); 41 other fixtures are byte-identical.

#380

Full Changelog: 0.7.0...0.7.1

0.7.0 - layout invariants, fragility reduction, and new directives

Choose a tag to compare

@pinin4fjords pinin4fjords released this 17 May 19:28
7a9520c

A big one! 228 commits since 0.6.1, with the layout engine considerably more robust and a handful of nice new directives to play with.

Existing .mmd files render with no changes - everything new here is additive.

Highlights

Embed your maps anywhere

nf-metro render mypipeline.mmd --format html now produces a self-contained, interactive HTML file you can drop straight into a docs page or a blog post (#374).

Three new %%metro directives

  • off_track: ... lifts file inputs above the line tracks so they sit clear of the metro lines instead of breaking them (#265). Great for FASTA/GTF inputs hanging off a workflow.
  • center_ports: true (or --center-ports) centres inter-section ports on the shorter section, which tidies up many fan-in/fan-out cases (#320).
  • legend_min_height: ... lets you reserve a minimum legend height so a tall map's legend doesn't look stranded.

Richer file icons

  • New files (stacked) and dir (folder) icon types, joining the existing single file (#175).
  • All three accept an optional caption: node[Label]:::files:Read 1, Read 2 to call out exactly what's flowing in (#269).

Optional / conditional paths

Lines can now be dashed or dotted to indicate optional or conditional routes through the pipeline. Add a fourth field to your %%metro line: directive:

%%metro line: qc | Quality Control | #ff8800 | dashed

Gallery additions

  • differentialabundance pipeline metro map (#319)
  • genomeassembly example
  • New topology fixtures: upward_bypass, mismatched_tracks, fan_in_merge
  • A new nf-core Pipelines gallery page on the docs site (#239)

Better-looking maps out of the box

  • Sections are now numbered in visual reading order, so "1, 2, 3..." matches how you actually scan the map (#217)
  • Fan-in merge junctions route cleanly onto the trunk (#207)
  • Per-line path grouping gives a consistent line z-order, so the colours stack predictably where lines cross (#305)
  • Animated balls no longer fly off-piste at merge junctions (#251)
  • Dozens of fan-out, fan-in, bypass, and off-track routing fixes - too many to list individually, but the cumulative effect on real pipelines is significant

Under the hood

This release also represents a substantial reliability push:

  • A layout invariant framework with phase-boundary guards (compute_layout(validate=True)), full-corpus parametrised tests, and the C13 row-gap runtime guard (#326, #371). When the layout engine misbehaves, you find out at the offending phase, not three phases later.
  • The layout pipeline is now organised into six named stages with flat Stage X.Y naming - much easier to reason about than the old monolithic phase 13 (#375, #376).
  • Major fragility-reduction series (#340 and follow-ups) tightening clearances, hoisting helpers, and documenting load-bearing phase pairs.
  • Performance work: spatial-index validation guards with closed-form intersection (#368), cached station_lines() (~40 call sites, O(1) amortised), pre-computed diamond groups.
  • Several large refactors splitting 300-500-line functions into focused helpers, deduplicating section iteration, unifying diagonal routing, and centralising corner-radius math.

Contributing

A new CONTRIBUTING.md covers the visual review workflow, the invariant ratchet, and git hygiene expectations (#341). If you've been meaning to file that "why does my map look like THIS?" issue, now's a good moment.

Full Changelog: 0.6.1...0.7.0

0.6.1 - docs build fix

Choose a tag to compare

@pinin4fjords pinin4fjords released this 06 Mar 17:57
d38d9db

What's Changed

  • fix: render variantbenchmarking and debug SVGs in docs build by @pinin4fjords in #172

Full Changelog: 0.6.0...0.6.1

0.6.0 - changes driven by variantbenchmarking example

Choose a tag to compare

@pinin4fjords pinin4fjords released this 06 Mar 17:44
95bc080

What's Changed

Full Changelog: 0.5.4...0.6.0

0.5.4 - animation fix

Choose a tag to compare

@pinin4fjords pinin4fjords released this 27 Feb 10:25
9d1bbe8

What's Changed

  • fix: synchronize animation timing and reduce diamond path explosion by @pinin4fjords in #168

Full Changelog: 0.5.3...0.5.4

0.5.3 - Increased section header prominence

Choose a tag to compare

@pinin4fjords pinin4fjords released this 27 Feb 09:19
02c1915

What's Changed

Full Changelog: 0.5.2...0.5.3

0.5.2 - layout tweaks for rnaseq

Choose a tag to compare

@pinin4fjords pinin4fjords released this 25 Feb 12:41
99c7a80

What's Changed

Full Changelog: 0.5.1...0.5.2