Skip to content

Channel Manager v1.0.60

Every TranscriptBrowserRenderer instance ships with its own channelManager, responsible for adding, removing, updating, and querying channels, as well as sorting, deduplication, and data export:

const cm = renderer.channelManager;

When you need a "standalone channel manager", you can also create one manually via new ChannelManager(delegate), but this is very rarely needed (see IChannelManagerDelegate for the shape of delegate).


Method

add

method

cm.add(config: IChannel): void

Appends a channel after showing a confirmation dialog.

addDirect

method

cm.addDirect(config: IChannel): void

Appends directly, skipping the dialog. Recommended for programmatic use.

addMany

method

cm.addMany(configs: IChannel[]): void

Adds multiple channels at once after showing a batch confirmation dialog. Note: addMany does not emit the "add" event itself directly (it fires only after the confirmation dialog flow completes).

cm.addDirect({
  name: "My BigWig",
  plot: "bar",
  data: { method: "remote", format: "bigwig", url: "https://.../signal.bw" },
});

get / findByName / indexOfName

method

cm.get(index: number): IChannel | undefined
cm.findByName(name: string): IChannel | undefined
cm.indexOfName(name: string): number

indexOfName returns -1 when not found.

findDuplicates

method

cm.findDuplicates(configs: IChannel[]): IChannel[]

Deduplication: returns the subset of configs that already exists in the current channel list. addChannel / addManyChannels call this method before showing the regular confirmation dialog; if duplicates are found, a "Channels already loaded — Force Load / Cancel" warning dialog is shown first.

Equality rule: two channels are considered duplicates when they share the same name and an identical data source (data.method + data.format + data.url all matching). Display fields such as style / group / plot / order do not participate in the comparison — re-importing the same source data with a different skin still counts as a duplicate.

Special case for local files (blob: URLs)

Local imports and drag-and-drop use URL.createObjectURL(file) to generate a new blob: URL — the URL string differs on every import of the same file. So equality has a special case: when both sides are blob: URLs, the URL comparison is skipped and equality is decided by name + format + method (the local import flow already encodes the file name into channel.name). If one side is a blob: URL and the other is a remote URL, they are refused as duplicates (the content cannot be proven equal).

Parameter Type Required Description
configs IChannel[] Candidate channel list (the ones about to be added)

Returns: IChannel[] — the duplicate subset of configs, preserving input order; returns [] when there are no duplicates.

Automatic within the add / addMany flow

You usually don't need to call findDuplicates manually — add() / addMany() already invoke it automatically before each addition and show the dialog as needed. This method is exposed for advanced integrations that "want to drive the UI themselves" (e.g. custom dialogs, batch background deduplication flows).

getAll / getConfig

method

cm.getAll(): IChannel[]
cm.getConfig(): IChannel[]

The two methods are equivalent (getConfig is an alias for getAll), returning a shallow copy of all current channel configs (in display order).

getDataByIndex / getDataByName / getDataList

method

cm.getDataByIndex(channelIndex: number): unknown[] | undefined
cm.getDataByName(channelName: string): unknown[] | undefined
cm.getDataList(): Array<{ channelIndex: number; channelName: string; channelData: unknown[] }>

Returns a channel's loaded runtime data (for export / downstream analysis). Note the parameter names are channelIndex / channelName.

update

method

cm.update(index: number, partial: Partial<IChannel>): boolean

Merges partial into the config of the specified channel and triggers a redraw.

cm.update(2, { style: { color: "#ff0000", height: 60 } });

remove

method

async cm.remove(index: number, silent = false): Promise<boolean>

silent = true skips the confirmation dialog (defaults to false, i.e. shows the confirmation dialog by default). Returns whether a deletion actually occurred.

removeByName

method

async cm.removeByName(name: string, silent = true): Promise<number>

Removes channels in bulk by name, returning the number deleted. silent defaults to true.

clear

method

async cm.clear(silent = true): Promise<void>

Clears all channels. silent defaults to true.

reorder / moveUp / moveDown

method

cm.reorder(fromIndex: number, toIndex: number): void
cm.moveUp(index: number): boolean
cm.moveDown(index: number): boolean

replaceAll

method

async cm.replaceAll(channels: IChannel[]): Promise<void>

Destroys all ChannelViews and rebuilds them from the new configs.

reload

method

cm.reload(): void

Makes all channels re-fetch data for the current visible region.

exportConfigs

method

cm.exportConfigs(): IChannel[]

A snapshot of channel configs in the current display order (re-stamps order based on the display index).

count

getter

cm.count: number

The current number of channels (a getter, not a method).

on

method

cm.on(listener: ChannelManagerListener): () => void

Subscribes to channel events, returning an unsubscribe function.

const off = cm.on((event, detail) => {
  console.log(event, detail?.channels?.length);
});
off();

Event

event

Event name Trigger
"add" After calling add() / addDirect() (addMany goes through the dialog and fires later)
"remove" After calling remove() / removeByName() (when ≥1 channel is actually deleted)
"update" After calling update()
"reorder" After calling reorder() / moveUp() / moveDown()
"clear" After calling clear()
"replace" After calling replaceAll()
"reload" After calling reload()

The detail of every event is { index?: number; channel?: IChannel; channels?: IChannel[] }. In particular, detail.channels is always populated with the full current channel list, making it easy to re-render directly.

ChannelManagerEvent / ChannelManagerListener

type

type ChannelManagerEvent =
  | "add"
  | "remove"
  | "update"
  | "reorder"
  | "clear"
  | "replace"
  | "reload";

type ChannelManagerListener = (
  event: ChannelManagerEvent,
  detail?: { index?: number; channel?: IChannel; channels?: IChannel[] },
) => void;

Types

IChannel

type

The complete configuration of a single channel (track).

interface IChannel {
  name: string;
  plot: string;
  level?: string;
  style?: IChannelStyle;
  group?: IChannelGroup;
  data?: IChannelData;
  filter?: string;
  pin?: boolean;
  /** Annotation config: field expansion, tooltip keys, separator, color mapping. */
  annotation?: IChannelAnnotation;
  /**
   * Display order. Sorting rules:
   * 1. Defaults to the config.channels array order
   * 2. Channels with an order come after those without
   * 3. Among channels with order, smaller values come first
   */
  order?: number;
  sortable?: boolean; // Whether sortable, defaults to true

  // ── Context-menu item visibility (each defaults to true; set to false to hide the item) ──
  isDeleteEnabled?: boolean;
  isSortEnabled?: boolean;
  isGroupEnabled?: boolean;
  isRenameEnabled?: boolean;
  isBackgroundColorEnabled?: boolean;
  isForegroundColorEnabled?: boolean;
  isChannelHeightEnabled?: boolean;
  isPinEnabled?: boolean;
  isImageDownloadEnabled?: boolean;
  isDataDownloadEnabled?: boolean;

  /**
   * When true (default), the channel obeys the style.detailZoom threshold: when the visible
   * region exceeds the threshold it shows a "Zoom in to view details" hint and **skips fetching
   * data**, reducing network requests. Set to false to ignore detailZoom and always fetch. Only
   * meaningful when style.detailZoom is set. Defaults to true.
   */
  isDetailLimitedByZoom?: boolean;
}

IChannelData

type

interface IChannelData {
  method: string;
  format: string;
  url?: string;
  index?: string;
  /** Secondary index file (e.g. the .gzi for a bgzip FASTA). */
  index2?: string;
  /**
   * Explicitly specify the BAM index variant ("bai" / "csi"). Only needed when the index URL has
   * no recognizable suffix (e.g. a drag-and-drop blob URL); otherwise it is inferred from the
   * index file-name suffix.
   */
  indexFormat?: "bai" | "csi";
  /**
   * Data source for method:"buildin" channels.
   * - "annotation": derive structure from the active reference's GFF3 annotation (transcript
   *   exon/CDS/UTR, gene exons, splice junctions), used for the auto-injected Transcript Structure /
   *   Exon / Junction.
   * - unset / other: use the legacy expression API of the defaultConfig.buildin template (e.g.
   *   GTEx exon / junction expression).
   */
  source?: "annotation" | string;
  /**
   * Inline raw content for method:"local". Pairs with format "tsv" (BED6/BED12) or "json" (a single
   * record or an array). When set, the URL is no longer fetched.
   */
  content?: string;
  retain?: boolean;
  /**
   * Aggregation function for multiple features within the same pixel bin in a BigWig/BigBed bar chart.
   * - 'mean': average within the bin (IGV default)
   * - 'max' / 'min': maximum / minimum within the bin
   * Only affects the score range label and normalization, not single-feature rendering. Defaults to 'mean'.
   */
  windowingFunction?: "mean" | "max" | "min";
}

IChannelStyle

type

All of a channel's display styling. Most fields are optional, with different defaults per plot type.

interface IChannelStyle {
  height?: number;
  /** Whether to pin (sticky) at the top of the viewport. axis is pinned by default. */
  pinned?: boolean;
  /** Maximum number of sub-channels visible when expanded (a multiple of the parent channel height), defaults to 5; a scrollbar appears beyond this. */
  maxHeight?: number;
  label?: IChannelLabelStyle;
  color?: string;
  paddingTop?: number;
  paddingBottom?: number;
  paddingLeft?: number;
  paddingRight?: number;
  cellWidth?: number;
  // ── Channel content styling ──
  expand?: string | string[];
  keys?: string[];
  /** Display order of key-value pairs in the tooltip. */
  order?: string[];
  sep?: string;
  foreground?:
    | string                 // Single color: "yellow", "#ff0000"
    | string[]               // Color array: ["sky", "yellow", "green"]
    | {
        // Colormap config for continuous data
        colormap?: string;   // 'viridis', 'plasma', 'coolwarm', etc.
        range?: [number, number];
        reversed?: boolean;
        custom?: { [key: string]: string };
        // ── Data-driven mapping specific to the diamond plot ──
        colorBy?: string;
        sizeBy?: string;
        sizeRange?: [number, number]; // Defaults to [0.1, 1]
        alpha?: number;               // Defaults to 1
        alphaBy?: string;
        alphaRange?: [number, number]; // Defaults to [0.1, 1]
        /** Filter diamond data by gene: the field name in record.rest that must match the current gene_id (version-stripped). */
        filterGene?: string;
        /** Filter by transcript: the field name in record.rest that must match the current trans_id (version-stripped). */
        filterTranscript?: string;
      };
  background?: string;       // Channel background color, defaults to #ffffff
  padding?: string;          // e.g. '0.2 0.1' (top bottom) or '0.2' (both)
  size?: number;             // Diamond plot size ratio: diamondPx = channelHeight * size, defaults to 0.3
  /**
   * Domain span threshold (bp); beyond it, details are hidden and a "Zoom in" hint is shown. Set 0
   * to disable (always show details). Per-plot defaults: bam 50000; transcript 500000; sequence
   * 10000; others 0.
   */
  detailZoom?: number;
  /** BAM coverage display config: a boolean toggle, or an object for fine-grained control. */
  coverage?:
    | boolean
    | {
        enabled?: boolean;                 // Defaults to true
        color?: string | { colormap?: string; range?: [number, number] };
        alpha?: number;                    // Defaults to 0.7
        height?: number;                   // Fraction of channel height, defaults to 0.2
        plot?: "bar" | "line" | "heatmap" | "point"; // Defaults to "bar"
        detailZoom?: number;               // Defaults to 5000000
        scoreLabel?: { enabled?: boolean; fontSize?: number; color?: string; background?: string };
      };
  /** BAM read strand colors: plus (+) / minus (−) / other (unknown). */
  readsColor?: { plus?: string; minus?: string; other?: string };
  /** Per-row height of lane stacking plots (variant / transcript), as a fraction of the content area, defaults to 0.31. */
  rowHeight?: number;
  /** In-row padding for variant lanes (a fraction of lane height, added top and bottom), defaults to 0.01. */
  rowPadding?: number;
  /** Variant base colors: A/T/C/G/N → hex. */
  baseColors?: { [base: string]: string };
  refColor?: string;
  baseTextColor?: string;
  /** Rendering config for RNA modification marks (m6A, m5C, etc.) on the transcript plot. */
  modification?: {
    enabled?: boolean;                     // Defaults to true
    color?: string | { colormap?: string; range?: [number, number] }; // Defaults to { colormap: "coolwarm" }
    alpha?: number;                        // Defaults to 1
    height?: number;                       // Fraction of channel height, defaults to 0.05
    plot?: "triangle";                     // Defaults to a downward triangle ▼
  };
  /** Whether junction arcs (score_curve) fill the area under the arc, defaults to false (stroke only). */
  areaShow?: boolean;
  /** Whether to auto-expand sub-channels when data first arrives, defaults to false. */
  isExpand?: boolean;
  /** Whether the rect plot (bigbed/bed) shows feature names as text labels, defaults to false. */
  showName?: boolean;
}

IChannelAnnotation

type

interface IChannelAnnotation {
  /** Field name used to expand (split) a feature into sub-channels. */
  expand?: string;
  /** Field keys to display in the tooltip / annotation. */
  keys?: string[];
  /** Separator used to split compound field values. */
  sep?: string;
  /**
   * Sub-channel color mapping:
   * - string: a single color name / hex
   * - string[]: a color array cycled across sub-channels
   * - { custom: Record<string, string> }: an explicit key → color mapping
   */
  colors?: string | string[] | { custom?: Record<string, string> };
  /** Filter expression applied to annotation records. */
  filter?: string;
}

IChannelLabelStyle

type

interface IChannelLabelStyle {
  color: string;
}

IChannelGroup

type

The shape of IChannel.group — used for grouping, stacked rendering, and group autoscale.

interface IChannelGroup {
  name: string;
  number?: number;
  /** Whether channels within the same number group are stacked (overlaid with alpha blending). If any channel in the group has stacked=true, the whole group is stacked. */
  stacked?: boolean;
  /** Per-channel alpha for stacked rendering (0.0–1.0, defaults to 0.8). */
  alpha?: number;
  /** Whether it belongs to a multi-select "Group Autoscale" group: members share the same value domain, kept in sync across pan/zoom/reorder. */
  autoscale?: boolean;
  /** The [min, max] value domain shared by the autoscale group, persisted to survive data reloads / config import-export. */
  autoscaleRange?: [number, number];
  style?: { color: string };
}

IChannelManagerDelegate

type

Needed only when manually constructing new ChannelManager(delegate) — it delegates channel CRUD back to the host renderer. No need to worry about it for regular use of renderer.channelManager.

interface IChannelManagerDelegate {
  addChannel(config: IChannel): void;
  addChannelDirect(config: IChannel): void;
  addManyChannels(configs: IChannel[]): void;
  removeChannelByIndex(index: number, silent?: boolean): Promise<boolean>;
  reorderChannels(fromIndex: number, toIndex: number): void;
  getChannelConfigs(): IChannel[];
  getChannelCount(): number;
  getChannelData(index: number): unknown[] | undefined;
  reloadAll(): void;
  replaceAllChannels(channels: IChannel[]): void;
  updateChannel(index: number): void;
}