@jbrowse/plugin-alignments 4.0.4 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,7 +14,7 @@ export default class CramSlightlyLazyFeature implements Feature {
14
14
  get flags(): number;
15
15
  get strand(): 1 | -1;
16
16
  get qual(): string;
17
- get qualRaw(): number[] | null | undefined;
17
+ get qualRaw(): Uint8Array<ArrayBufferLike> | null | undefined;
18
18
  get refName(): string;
19
19
  get pair_orientation(): string | null | undefined;
20
20
  get template_length(): number | undefined;
@@ -5,7 +5,7 @@ import { SimpleFeature, getContainingTrack, getContainingView, getSession, isSes
5
5
  import { getRpcSessionId } from '@jbrowse/core/util/tracks';
6
6
  import { cast, isAlive, types } from '@jbrowse/mobx-state-tree';
7
7
  import { BaseLinearDisplay } from '@jbrowse/plugin-linear-genome-view';
8
- import FilterListIcon from '@mui/icons-material/ClearAll';
8
+ import ClearAllIcon from '@mui/icons-material/ClearAll';
9
9
  import ContentCopyIcon from '@mui/icons-material/ContentCopy';
10
10
  import MenuOpenIcon from '@mui/icons-material/MenuOpen';
11
11
  import VisibilityIcon from '@mui/icons-material/Visibility';
@@ -459,7 +459,7 @@ export function SharedLinearPileupDisplayMixin(configSchema) {
459
459
  },
460
460
  {
461
461
  label: 'Filter by...',
462
- icon: FilterListIcon,
462
+ icon: ClearAllIcon,
463
463
  onClick: () => {
464
464
  getSession(self).queueDialog(handleClose => [
465
465
  FilterByTagDialog,
@@ -1,27 +1,115 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
3
  import { Dialog, ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
4
4
  import { getContainingTrack, getContainingView, getSession, useDebounce, } from '@jbrowse/core/util';
5
- import { getSnapshot } from '@jbrowse/mobx-state-tree';
5
+ import { getSnapshot, isStateTreeNode } from '@jbrowse/mobx-state-tree';
6
6
  import { Button, DialogActions, DialogContent, MenuItem, TextField, Typography, } from '@mui/material';
7
7
  import { observer } from 'mobx-react';
8
8
  import { getUniqueTags } from "../../shared/getUniqueTags.js";
9
9
  import { defaultFilterFlags, negFlags, posFlags } from "../../shared/util.js";
10
+ const TAG_REGEX = /^[A-Za-z][A-Za-z0-9]$/;
11
+ function TagResults({ tag, tagSet }) {
12
+ if (tagSet.length === 0) {
13
+ return (_jsxs(Typography, { color: "warning.main", children: ["No values found for tag ", tag, " in the current region"] }));
14
+ }
15
+ return (_jsxs("div", { children: [_jsxs("div", { children: ["Found unique ", tag, " values:"] }), _jsx("div", { children: tagSet.join(', ') })] }));
16
+ }
17
+ function createTrackId(baseId, suffix) {
18
+ return `${baseId}-${suffix}-${Date.now()}-sessionTrack`;
19
+ }
20
+ function createTagBasedTracks({ trackConf, tag, tagSet, session, view, }) {
21
+ const values = [...tagSet, undefined];
22
+ for (const tagValue of values) {
23
+ const trackId = createTrackId(trackConf.trackId, `${tag}:${tagValue}`);
24
+ session.addTrackConf({
25
+ ...trackConf,
26
+ trackId,
27
+ name: `${trackConf.name} (${tag}:${tagValue})`,
28
+ displays: [
29
+ {
30
+ displayId: `${trackId}-LinearAlignmentsDisplay`,
31
+ type: 'LinearAlignmentsDisplay',
32
+ pileupDisplay: {
33
+ displayId: `${trackId}-LinearAlignmentsDisplay-LinearPileupDisplay`,
34
+ type: 'LinearPileupDisplay',
35
+ filterBy: {
36
+ ...defaultFilterFlags,
37
+ tagFilter: {
38
+ tag,
39
+ value: tagValue,
40
+ },
41
+ },
42
+ },
43
+ },
44
+ ],
45
+ });
46
+ view.showTrack(trackId);
47
+ }
48
+ }
49
+ function createStrandBasedTracks({ trackConf, session, view, }) {
50
+ const negTrackId = createTrackId(trackConf.trackId, 'strand:(-)');
51
+ const posTrackId = createTrackId(trackConf.trackId, 'strand:(+)');
52
+ session.addTrackConf({
53
+ ...trackConf,
54
+ trackId: negTrackId,
55
+ name: `${trackConf.name} (-)`,
56
+ displays: [
57
+ {
58
+ displayId: `${negTrackId}-LinearAlignmentsDisplay`,
59
+ type: 'LinearAlignmentsDisplay',
60
+ pileupDisplay: {
61
+ displayId: `${negTrackId}-LinearAlignmentsDisplay-LinearPileupDisplay`,
62
+ type: 'LinearPileupDisplay',
63
+ filterBy: negFlags,
64
+ },
65
+ },
66
+ {
67
+ displayId: `${negTrackId}-LinearSNPCoverageDisplay`,
68
+ type: 'LinearSNPCoverageDisplay',
69
+ filterBy: negFlags,
70
+ },
71
+ ],
72
+ });
73
+ session.addTrackConf({
74
+ ...trackConf,
75
+ trackId: posTrackId,
76
+ name: `${trackConf.name} (+)`,
77
+ displays: [
78
+ {
79
+ displayId: `${posTrackId}-LinearAlignmentsDisplay`,
80
+ type: 'LinearAlignmentsDisplay',
81
+ pileupDisplay: {
82
+ displayId: `${posTrackId}-LinearAlignmentsDisplay-LinearPileupDisplay`,
83
+ type: 'LinearPileupDisplay',
84
+ filterBy: posFlags,
85
+ },
86
+ },
87
+ {
88
+ displayId: `${posTrackId}-LinearSNPCoverageDisplay`,
89
+ type: 'LinearSNPCoverageDisplay',
90
+ filterBy: posFlags,
91
+ },
92
+ ],
93
+ });
94
+ view.showTrack(negTrackId);
95
+ view.showTrack(posTrackId);
96
+ }
10
97
  const GroupByTagDialog = observer(function GroupByTagDialog(props) {
11
98
  const { model, handleClose } = props;
12
99
  const [tag, setGroupByTag] = useState('');
13
100
  const [tagSet, setGroupByTagSet] = useState();
14
101
  const [loading, setLoading] = useState(false);
15
102
  const [error, setError] = useState();
16
- const validTag = /^[A-Za-z][A-Za-z0-9]$/.exec(tag);
103
+ const validTag = TAG_REGEX.exec(tag);
17
104
  const isInvalid = tag.length === 2 && !validTag;
18
105
  const debouncedTag = useDebounce(tag, 1000);
19
106
  const [type, setType] = useState('');
20
107
  useEffect(() => {
21
108
  ;
22
109
  (async () => {
23
- try {
24
- if (!isInvalid) {
110
+ const isValidTag = TAG_REGEX.test(debouncedTag);
111
+ if (type === 'tag' && isValidTag) {
112
+ try {
25
113
  setError(undefined);
26
114
  setLoading(true);
27
115
  const vals = await getUniqueTags({
@@ -32,16 +120,23 @@ const GroupByTagDialog = observer(function GroupByTagDialog(props) {
32
120
  });
33
121
  setGroupByTagSet(vals);
34
122
  }
35
- }
36
- catch (e) {
37
- console.error(e);
38
- setError(e);
39
- }
40
- finally {
41
- setLoading(false);
123
+ catch (e) {
124
+ console.error(e);
125
+ setError(e);
126
+ }
127
+ finally {
128
+ setLoading(false);
129
+ }
42
130
  }
43
131
  })();
44
- }, [model, isInvalid, debouncedTag]);
132
+ }, [model, type, debouncedTag]);
133
+ useEffect(() => {
134
+ if (type !== 'tag') {
135
+ setGroupByTagSet(undefined);
136
+ setError(undefined);
137
+ setLoading(false);
138
+ }
139
+ }, [type]);
45
140
  return (_jsxs(Dialog, { open: true, onClose: handleClose, title: "Group by", children: [_jsxs(DialogContent, { children: [_jsx(Typography, { children: "NOTE: this will create new session tracks with the \"filter by\" set to the values chosen here rather than affecting the current track state" }), _jsxs(TextField, { fullWidth: true, value: type, onChange: event => {
46
141
  setType(event.target.value);
47
142
  }, label: "Group by...", select: true, children: [_jsx(MenuItem, { value: "strand", children: "Strand" }), _jsx(MenuItem, { value: "tag", children: "Tag" })] }), type === 'tag' ? (_jsxs(_Fragment, { children: [_jsx(Typography, { color: "textSecondary", children: "Examples: HP for haplotype, RG for read group, etc." }), _jsx(TextField, { value: tag, onChange: event => {
@@ -51,93 +146,32 @@ const GroupByTagDialog = observer(function GroupByTagDialog(props) {
51
146
  maxLength: 2,
52
147
  'data-testid': 'group-tag-name-input',
53
148
  },
54
- } }), error ? (_jsx(ErrorMessage, { error: error })) : loading ? (_jsx(LoadingEllipses, { message: "Loading unique tags" })) : tagSet ? (_jsxs("div", { children: [_jsxs("div", { children: ["Found unique ", tag, " values:"] }), _jsx("div", { children: tagSet.join(', ') })] })) : null] })) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", color: "primary", type: "submit", disabled: !tagSet, autoFocus: true, onClick: () => {
149
+ } }), error ? (_jsx(ErrorMessage, { error: error })) : loading ? (_jsx(LoadingEllipses, { message: "Loading unique tags" })) : tagSet ? (_jsx(TagResults, { tag: tag, tagSet: tagSet })) : null] })) : null] }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", color: "primary", type: "submit", disabled: !type ||
150
+ loading ||
151
+ (type === 'tag' && (!tagSet || tagSet.length === 0)), autoFocus: true, onClick: () => {
55
152
  const track = getContainingTrack(model);
56
- const trackConf = structuredClone(getSnapshot(track.configuration));
153
+ const trackConf = isStateTreeNode(track.configuration)
154
+ ? structuredClone(getSnapshot(track.configuration))
155
+ : track.configuration;
57
156
  const session = getSession(model);
58
157
  const view = getContainingView(model);
59
- if (type === 'tag') {
60
- if (tagSet) {
61
- const ret = [...tagSet, undefined];
62
- for (const tagValue of ret) {
63
- const t1 = `${trackConf.trackId}-${tag}:${tagValue}-${Date.now()}-sessionTrack`;
64
- session.addTrackConf({
65
- ...trackConf,
66
- trackId: t1,
67
- name: `${trackConf.name} (${tag}:${tagValue})`,
68
- displays: [
69
- {
70
- displayId: `${t1}-LinearAlignmentsDisplay`,
71
- type: 'LinearAlignmentsDisplay',
72
- pileupDisplay: {
73
- displayId: `${t1}-LinearAlignmentsDisplay-LinearPileupDisplay`,
74
- type: 'LinearPileupDisplay',
75
- filterBy: {
76
- ...defaultFilterFlags,
77
- tagFilter: {
78
- tag,
79
- value: tagValue,
80
- },
81
- },
82
- },
83
- },
84
- ],
85
- });
86
- view.showTrack(t1);
87
- }
88
- }
158
+ if (type === 'tag' && tagSet) {
159
+ createTagBasedTracks({
160
+ trackConf,
161
+ tag,
162
+ tagSet,
163
+ session,
164
+ view,
165
+ });
89
166
  }
90
167
  else if (type === 'strand') {
91
- const t1 = `${trackConf.trackId}-${tag}:(-)-${Date.now()}-sessionTrack`;
92
- const t2 = `${trackConf.trackId}-${tag}:(+)-${Date.now()}-sessionTrack`;
93
- session.addTrackConf({
94
- ...trackConf,
95
- trackId: t1,
96
- name: `${trackConf.name} (-)`,
97
- displays: [
98
- {
99
- displayId: `${t1}-LinearAlignmentsDisplay`,
100
- type: 'LinearAlignmentsDisplay',
101
- pileupDisplay: {
102
- displayId: `${t1}-LinearAlignmentsDisplay-LinearPileupDisplay`,
103
- type: 'LinearPileupDisplay',
104
- filterBy: negFlags,
105
- },
106
- },
107
- {
108
- displayId: `${t1}-LinearSNPCoverageDisplay`,
109
- type: 'LinearSNPCoverageDisplay',
110
- filterBy: negFlags,
111
- },
112
- ],
168
+ createStrandBasedTracks({
169
+ trackConf,
170
+ session,
171
+ view,
113
172
  });
114
- session.addTrackConf({
115
- ...trackConf,
116
- trackId: t2,
117
- name: `${trackConf.name} (+)`,
118
- displays: [
119
- {
120
- displayId: `${t2}-LinearAlignmentsDisplay`,
121
- type: 'LinearAlignmentsDisplay',
122
- pileupDisplay: {
123
- displayId: `${t2}-LinearAlignmentsDisplay-LinearPileupDisplay`,
124
- type: 'LinearPileupDisplay',
125
- filterBy: posFlags,
126
- },
127
- },
128
- {
129
- displayId: `${t2}-LinearSNPCoverageDisplay`,
130
- type: 'LinearSNPCoverageDisplay',
131
- filterBy: posFlags,
132
- },
133
- ],
134
- });
135
- view.showTrack(t1);
136
- view.showTrack(t2);
137
173
  }
138
174
  handleClose();
139
- }, children: "Submit" }), _jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
140
- handleClose();
141
- }, children: "Cancel" })] })] }));
175
+ }, children: "Submit" }), _jsx(Button, { variant: "contained", color: "secondary", onClick: handleClose, children: "Cancel" })] })] }));
142
176
  });
143
177
  export default GroupByTagDialog;
@@ -233,19 +233,24 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
233
233
  } & {
234
234
  loading: boolean;
235
235
  lastDrawnOffsetPx: number | undefined;
236
+ lastDrawnBpPerPx: number | undefined;
236
237
  ref: HTMLCanvasElement | null;
237
238
  renderingImageData: ImageBitmap | undefined;
238
239
  renderingStopToken: import("@jbrowse/core/util").StopToken | undefined;
239
240
  statusMessage: string | undefined;
241
+ canvasDrawn: boolean;
240
242
  } & {
241
243
  readonly drawn: boolean;
244
+ readonly fullyDrawn: boolean;
242
245
  } & {
243
246
  setLastDrawnOffsetPx(n: number): void;
247
+ setLastDrawnBpPerPx(n: number): void;
244
248
  setLoading(f: boolean): void;
245
249
  setRef(ref: HTMLCanvasElement | null): void;
246
250
  setRenderingImageData(imageData: ImageBitmap | undefined): void;
247
251
  setRenderingStopToken(token?: import("@jbrowse/core/util").StopToken): void;
248
252
  setStatusMessage(msg?: string): void;
253
+ setCanvasDrawn(drawn: boolean): void;
249
254
  } & {
250
255
  beforeDestroy(): void;
251
256
  } & {
@@ -236,17 +236,16 @@ const Cloud = observer(function Cloud({ model, }) {
236
236
  width,
237
237
  height,
238
238
  cursor: hasHover ? 'pointer' : undefined,
239
- }, onMouseMove: onMouseMove, onMouseLeave: onMouseLeave, onClick: onClick, children: [_jsx(CloudCanvases, { model: model, width: width, height: height }), _jsx(FeatureHighlights, { selectedFeatureBounds: selectedFeatureBounds, hoveredFeature: hoveredFeature }), model.drawCloud && model.cloudTicks ? (_jsx("svg", { style: {
239
+ }, onMouseMove: onMouseMove, onMouseLeave: onMouseLeave, onClick: onClick, children: [_jsx(CloudCanvases, { model: model, width: width, height: height }), _jsx(FeatureHighlights, { selectedFeatureBounds: selectedFeatureBounds, hoveredFeature: hoveredFeature }), hoveredMismatchData && mousePosition ? (_jsx(MismatchTooltip, { mismatchData: hoveredMismatchData, mousePosition: mousePosition })) : hoveredFeatureData && mousePosition ? (_jsx(FeatureTooltip, { hoveredFeatureData: hoveredFeatureData, hasSupplementary: hoveredHasSupplementary, mousePosition: mousePosition })) : null] }));
240
+ });
241
+ const LinearReadCloudReactComponent = observer(function LinearReadCloudReactComponent({ model, }) {
242
+ return (_jsxs("div", { children: [_jsx(BaseDisplayComponent, { model: model, children: _jsx(Cloud, { model: model }) }), model.drawCloud && model.cloudTicks ? (_jsx("svg", { style: {
240
243
  position: 'absolute',
241
244
  top: 0,
242
245
  left: 50,
243
246
  pointerEvents: 'none',
244
247
  height: model.cloudTicks.height,
245
- width: 60,
246
- zIndex: 100,
247
- }, children: _jsx("g", { transform: "translate(55, 0)", children: _jsx(CloudYScaleBar, { model: model, orientation: "left" }) }) })) : null, hoveredMismatchData && mousePosition ? (_jsx(MismatchTooltip, { mismatchData: hoveredMismatchData, mousePosition: mousePosition })) : hoveredFeatureData && mousePosition ? (_jsx(FeatureTooltip, { hoveredFeatureData: hoveredFeatureData, hasSupplementary: hoveredHasSupplementary, mousePosition: mousePosition })) : null] }));
248
- });
249
- const LinearReadCloudReactComponent = observer(function LinearReadCloudReactComponent({ model, }) {
250
- return (_jsx(BaseDisplayComponent, { model: model, children: _jsx(Cloud, { model: model }) }));
248
+ width: 50,
249
+ }, children: _jsx("g", { transform: "translate(45, 0)", children: _jsx(CloudYScaleBar, { model: model, orientation: "left" }) }) })) : null] }));
251
250
  });
252
251
  export default LinearReadCloudReactComponent;
@@ -242,19 +242,24 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
242
242
  } & {
243
243
  loading: boolean;
244
244
  lastDrawnOffsetPx: number | undefined;
245
+ lastDrawnBpPerPx: number | undefined;
245
246
  ref: HTMLCanvasElement | null;
246
247
  renderingImageData: ImageBitmap | undefined;
247
248
  renderingStopToken: import("@jbrowse/core/util").StopToken | undefined;
248
249
  statusMessage: string | undefined;
250
+ canvasDrawn: boolean;
249
251
  } & {
250
252
  readonly drawn: boolean;
253
+ readonly fullyDrawn: boolean;
251
254
  } & {
252
255
  setLastDrawnOffsetPx(n: number): void;
256
+ setLastDrawnBpPerPx(n: number): void;
253
257
  setLoading(f: boolean): void;
254
258
  setRef(ref: HTMLCanvasElement | null): void;
255
259
  setRenderingImageData(imageData: ImageBitmap | undefined): void;
256
260
  setRenderingStopToken(token?: import("@jbrowse/core/util").StopToken): void;
257
261
  setStatusMessage(msg?: string): void;
262
+ setCanvasDrawn(drawn: boolean): void;
258
263
  } & {
259
264
  beforeDestroy(): void;
260
265
  } & {
@@ -311,8 +316,6 @@ declare function stateModelFactory(configSchema: AnyConfigurationSchemaType): im
311
316
  readonly hideLargeIndels: any;
312
317
  } & {
313
318
  readonly modificationThreshold: any;
314
- readonly cloudDomain: [number, number] | undefined;
315
- } & {
316
319
  readonly cloudTicks: import("../RenderLinearReadCloudDisplayRPC/drawFeatsCloud.ts").CloudTicks | undefined;
317
320
  } & {
318
321
  setNoSpacing(flag?: boolean): void;
@@ -68,19 +68,13 @@ function stateModelFactory(configSchema) {
68
68
  get modificationThreshold() {
69
69
  return self.colorBy?.modifications?.threshold ?? 10;
70
70
  },
71
- get cloudDomain() {
72
- if (self.cloudMaxDistance === undefined) {
73
- return undefined;
74
- }
75
- return [1, self.cloudMaxDistance];
76
- },
77
- }))
78
- .views(self => ({
79
71
  get cloudTicks() {
80
- if (!self.drawCloud || !self.cloudDomain || !self.showYScalebar) {
72
+ if (!self.drawCloud ||
73
+ self.cloudMaxDistance === undefined ||
74
+ !self.showYScalebar) {
81
75
  return undefined;
82
76
  }
83
- return calculateCloudTicks(self.cloudDomain, self.height);
77
+ return calculateCloudTicks(self.cloudMaxDistance, self.height);
84
78
  },
85
79
  }))
86
80
  .actions(self => ({
@@ -166,7 +160,6 @@ function stateModelFactory(configSchema) {
166
160
  hideMismatches: self.hideMismatches,
167
161
  hideLargeIndels: self.hideLargeIndels,
168
162
  showOutline: self.showOutline,
169
- cloudDomain: self.cloudDomain,
170
163
  visibleModifications: Object.fromEntries(self.visibleModifications.toJSON()),
171
164
  };
172
165
  },
@@ -37,11 +37,10 @@ export async function renderSvg(self, opts) {
37
37
  const visibleWidth = view.width;
38
38
  const clipId = `clip-${self.id}-svg`;
39
39
  const legendItems = self.showLegend ? self.legendItems() : [];
40
- const cloudDomain = rendering.cloudMaxDistance !== undefined && self.drawCloud
41
- ? [1, rendering.cloudMaxDistance]
42
- : null;
43
- const cloudTicks = cloudDomain && self.showYScalebar
44
- ? calculateCloudTicks(cloudDomain, height)
40
+ const cloudTicks = rendering.cloudMaxDistance !== undefined &&
41
+ self.drawCloud &&
42
+ self.showYScalebar
43
+ ? calculateCloudTicks(rendering.cloudMaxDistance, height)
45
44
  : null;
46
45
  return (_jsxs(_Fragment, { children: [_jsx("defs", { children: _jsx("clipPath", { id: clipId, children: _jsx("rect", { x: 0, y: 0, width: visibleWidth, height: height }) }) }), _jsx("g", { clipPath: `url(#${clipId})`, children: _jsx("g", { transform: `translate(${Math.max(0, -view.offsetPx)} 0)`, children: _jsx(ReactRendering, { rendering: finalRendering }) }) }), cloudTicks ? (_jsx("g", { transform: `translate(${Math.max(-view.offsetPx, 0)})`, children: _jsx(CloudYScaleBar, { model: { cloudTicks }, orientation: "left" }) })) : null, legendItems.length > 0 ? (_jsx(SVGLegend, { items: legendItems, width: visibleWidth, legendAreaWidth: opts.legendWidth })) : null] }));
47
46
  }
@@ -4,7 +4,7 @@ import SerializableFilterChain from '@jbrowse/core/pluggableElementTypes/rendere
4
4
  import { getContainingView, getSession } from '@jbrowse/core/util';
5
5
  import { cast, getSnapshot, isAlive, types } from '@jbrowse/mobx-state-tree';
6
6
  import { linearWiggleDisplayModelFactory } from '@jbrowse/plugin-wiggle';
7
- import FilterListIcon from '@mui/icons-material/FilterList';
7
+ import ClearAllIcon from '@mui/icons-material/ClearAll';
8
8
  import VisibilityIcon from '@mui/icons-material/Visibility';
9
9
  import { SharedModificationsMixin } from "../shared/SharedModificationsMixin.js";
10
10
  import { getUniqueModifications } from "../shared/getUniqueModifications.js";
@@ -268,7 +268,7 @@ function stateModelFactory(pluginManager, configSchema) {
268
268
  },
269
269
  {
270
270
  label: 'Filter arcs by score...',
271
- icon: FilterListIcon,
271
+ icon: ClearAllIcon,
272
272
  onClick: () => {
273
273
  getSession(self).queueDialog(handleClose => [
274
274
  FilterArcsByScoreDialog,
@@ -20,7 +20,6 @@ export interface RenderLinearReadCloudDisplayArgs {
20
20
  flipStrandLongReadChains: boolean;
21
21
  trackMaxHeight?: number;
22
22
  cloudModeHeight?: number;
23
- cloudDomain?: [number, number];
24
23
  highResolutionScaling?: number;
25
24
  exportSVG?: {
26
25
  rasterizeLayers?: boolean;
@@ -6,12 +6,11 @@ export interface CloudTicks {
6
6
  y: number;
7
7
  }[];
8
8
  height: number;
9
- minDistance: number;
10
9
  maxDistance: number;
11
10
  }
12
- export declare function createCloudScale(minDistance: number, maxDistance: number, height: number): import("d3-scale").ScaleLogarithmic<number, number, never>;
13
- export declare function calculateCloudTicks(domain: [number, number], height: number): CloudTicks;
14
- export declare function calculateCloudYOffsetsUtil(computedChains: ComputedChain[], height: number, cloudDomain?: [number, number]): {
11
+ export declare function createCloudScale(maxDistance: number, height: number): import("d3-scale").ScaleLogarithmic<number, number, never>;
12
+ export declare function calculateCloudTicks(maxDistance: number, height: number): CloudTicks;
13
+ export declare function calculateCloudYOffsetsUtil(computedChains: ComputedChain[], height: number): {
15
14
  chainYOffsets: Map<string, number>;
16
15
  cloudMaxDistance: number;
17
16
  };
@@ -1,37 +1,24 @@
1
1
  import { scaleLog } from '@mui/x-charts-vendor/d3-scale';
2
2
  export const CLOUD_HEIGHT_PADDING = 20;
3
3
  const DEFAULT_MAX_DISTANCE = 1000;
4
- export function createCloudScale(minDistance, maxDistance, height) {
4
+ export function createCloudScale(maxDistance, height) {
5
5
  return scaleLog()
6
6
  .base(2)
7
- .domain([Math.max(1, minDistance), Math.max(2, maxDistance)])
7
+ .domain([1, Math.max(2, maxDistance)])
8
8
  .range([0, height - CLOUD_HEIGHT_PADDING])
9
9
  .clamp(true);
10
10
  }
11
- export function calculateCloudTicks(domain, height) {
12
- const [minDistance, maxDistance] = domain;
13
- const scale = createCloudScale(minDistance, maxDistance, height);
11
+ export function calculateCloudTicks(maxDistance, height) {
12
+ const scale = createCloudScale(maxDistance, height);
14
13
  const tickValues = scale.ticks(6);
15
14
  const ticks = tickValues.map(value => ({
16
15
  value,
17
16
  y: scale(value),
18
17
  }));
19
- return { ticks, height, minDistance, maxDistance };
18
+ return { ticks, height, maxDistance };
20
19
  }
21
- export function calculateCloudYOffsetsUtil(computedChains, height, cloudDomain) {
20
+ export function calculateCloudYOffsetsUtil(computedChains, height) {
22
21
  const chainYOffsets = new Map();
23
- if (cloudDomain) {
24
- const [, maxDistance] = cloudDomain;
25
- const scale = createCloudScale(1, maxDistance, height);
26
- for (const { id, distance } of computedChains) {
27
- const top = distance > 0 ? scale(distance) : 0;
28
- chainYOffsets.set(id, top);
29
- }
30
- return {
31
- chainYOffsets,
32
- cloudMaxDistance: maxDistance,
33
- };
34
- }
35
22
  let maxDistance = Number.MIN_VALUE;
36
23
  for (const { distance } of computedChains) {
37
24
  if (distance > 0) {
@@ -41,7 +28,7 @@ export function calculateCloudYOffsetsUtil(computedChains, height, cloudDomain)
41
28
  if (maxDistance === Number.MIN_VALUE) {
42
29
  maxDistance = DEFAULT_MAX_DISTANCE;
43
30
  }
44
- const scale = createCloudScale(1, maxDistance, height);
31
+ const scale = createCloudScale(maxDistance, height);
45
32
  for (const { id, distance } of computedChains) {
46
33
  const top = distance > 0 ? scale(distance) : 0;
47
34
  chainYOffsets.set(id, top);
@@ -12,7 +12,7 @@ import { computeChainBounds, drawFeatsCore, filterChains, sortComputedChains, }
12
12
  import { calculateStackYOffsetsUtil } from "./drawFeatsStack.js";
13
13
  import { getInsertSizeStats } from "../shared/insertSizeStats.js";
14
14
  export async function executeRenderLinearReadCloudDisplay({ pluginManager, args, }) {
15
- const { sessionId, view: viewSnapshot, adapterConfig, sequenceAdapter, config, theme, featureHeight, noSpacing, drawCloud, colorBy, drawSingletons, drawProperPairs, flipStrandLongReadChains, trackMaxHeight, cloudModeHeight, cloudDomain, highResolutionScaling, exportSVG, statusCallback = () => { }, stopToken, visibleModifications, hideSmallIndels, hideMismatches, hideLargeIndels, showOutline, } = args;
15
+ const { sessionId, view: viewSnapshot, adapterConfig, sequenceAdapter, config, theme, featureHeight, noSpacing, drawCloud, colorBy, drawSingletons, drawProperPairs, flipStrandLongReadChains, trackMaxHeight, cloudModeHeight, highResolutionScaling, exportSVG, statusCallback = () => { }, stopToken, visibleModifications, hideSmallIndels, hideMismatches, hideLargeIndels, showOutline, } = args;
16
16
  const view = Base1DView.create(viewSnapshot);
17
17
  if (viewSnapshot.width) {
18
18
  view.setVolatileWidth(viewSnapshot.width);
@@ -125,7 +125,7 @@ export async function executeRenderLinearReadCloudDisplay({ pluginManager, args,
125
125
  view: viewSnap,
126
126
  calculateYOffsets: (chains) => {
127
127
  return drawCloud
128
- ? calculateCloudYOffsetsUtil(chains, actualHeight, cloudDomain)
128
+ ? calculateCloudYOffsetsUtil(chains, actualHeight)
129
129
  : calculateStackYOffsetsUtil(chains, featureHeight, noSpacing, trackMaxHeight ?? 1200);
130
130
  },
131
131
  });
@@ -5,19 +5,24 @@ export declare function LinearReadDisplayBaseMixin(): import("@jbrowse/mobx-stat
5
5
  }, {
6
6
  loading: boolean;
7
7
  lastDrawnOffsetPx: number | undefined;
8
+ lastDrawnBpPerPx: number | undefined;
8
9
  ref: HTMLCanvasElement | null;
9
10
  renderingImageData: ImageBitmap | undefined;
10
11
  renderingStopToken: import("@jbrowse/core/util").StopToken | undefined;
11
12
  statusMessage: string | undefined;
13
+ canvasDrawn: boolean;
12
14
  } & {
13
15
  readonly drawn: boolean;
16
+ readonly fullyDrawn: boolean;
14
17
  } & {
15
18
  setLastDrawnOffsetPx(n: number): void;
19
+ setLastDrawnBpPerPx(n: number): void;
16
20
  setLoading(f: boolean): void;
17
21
  setRef(ref: HTMLCanvasElement | null): void;
18
22
  setRenderingImageData(imageData: ImageBitmap | undefined): void;
19
23
  setRenderingStopToken(token?: import("@jbrowse/core/util").StopToken): void;
20
24
  setStatusMessage(msg?: string): void;
25
+ setCanvasDrawn(drawn: boolean): void;
21
26
  } & {
22
27
  beforeDestroy(): void;
23
28
  } & {
@@ -20,6 +20,7 @@ export interface RPCRenderableModel {
20
20
  ref: HTMLCanvasElement | null;
21
21
  renderingImageData?: ImageBitmap;
22
22
  setStatusMessage?: (msg: string) => void;
23
+ setCanvasDrawn?: (drawn: boolean) => void;
23
24
  }
24
25
  export interface RPCRenderSetupParams<T extends RPCRenderableModel, R = Record<string, unknown>> {
25
26
  self: T;
@@ -33,6 +33,7 @@ export function createRPCRenderFunction({ self, rpcMethodName, getRPCParams, onR
33
33
  const stopToken = createStopToken();
34
34
  self.setRenderingStopToken(stopToken);
35
35
  self.setLoading(true);
36
+ self.setCanvasDrawn?.(false);
36
37
  const viewSnapshot = {
37
38
  displayedRegions: structuredClone(view.displayedRegions),
38
39
  bpPerPx: view.bpPerPx,
@@ -69,6 +70,7 @@ export function createRPCRenderFunction({ self, rpcMethodName, getRPCParams, onR
69
70
  }
70
71
  catch (error) {
71
72
  if (!isAbortException(error)) {
73
+ console.error(error);
72
74
  self.setError(error);
73
75
  }
74
76
  }
@@ -80,7 +82,10 @@ export function createRPCRenderFunction({ self, rpcMethodName, getRPCParams, onR
80
82
  }
81
83
  export function setupCanvasRenderingAutorun(self) {
82
84
  createAutorun(self, async () => {
83
- drawCanvasImageData(self.ref, self.renderingImageData);
85
+ const success = drawCanvasImageData(self.ref, self.renderingImageData);
86
+ if (isAlive(self)) {
87
+ self.setCanvasDrawn?.(success);
88
+ }
84
89
  }, {
85
90
  name: 'CanvasRenderAutorun',
86
91
  });
@@ -1,6 +1,6 @@
1
1
  import { lazy } from 'react';
2
2
  import { getSession } from '@jbrowse/core/util';
3
- import FilterListIcon from '@mui/icons-material/ClearAll';
3
+ import ClearAllIcon from '@mui/icons-material/ClearAll';
4
4
  import PaletteIcon from '@mui/icons-material/Palette';
5
5
  import { modificationData } from "./modificationData.js";
6
6
  const FilterByTagDialog = lazy(() => import("./components/FilterByTagDialog.js"));
@@ -150,7 +150,7 @@ export function getColorSchemeMenuItem(model) {
150
150
  export function getFilterByMenuItem(model) {
151
151
  return {
152
152
  label: 'Filter by',
153
- icon: FilterListIcon,
153
+ icon: ClearAllIcon,
154
154
  onClick: () => {
155
155
  getSession(model).queueDialog((handleClose) => [
156
156
  FilterByTagDialog,
@@ -162,7 +162,7 @@ export function getFilterByMenuItem(model) {
162
162
  export function getEditFiltersMenuItem(model) {
163
163
  return {
164
164
  label: 'Edit filters',
165
- icon: FilterListIcon,
165
+ icon: ClearAllIcon,
166
166
  type: 'subMenu',
167
167
  subMenu: [
168
168
  {
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@jbrowse/plugin-alignments",
3
- "version": "4.0.4",
3
+ "version": "4.1.1",
4
+ "type": "module",
4
5
  "description": "JBrowse 2 alignments adapters, tracks, etc.",
5
6
  "keywords": [
6
7
  "jbrowse",
@@ -21,11 +22,11 @@
21
22
  ],
22
23
  "dependencies": {
23
24
  "@gmod/bam": "^7.1.15",
24
- "@gmod/cram": "^7.0.3",
25
+ "@gmod/cram": "^8.0.0",
25
26
  "@jbrowse/mobx-state-tree": "^5.5.0",
26
- "@mui/icons-material": "^7.3.6",
27
- "@mui/material": "^7.3.6",
28
- "@mui/x-charts-vendor": "^8.25.0",
27
+ "@mui/icons-material": "^7.3.7",
28
+ "@mui/material": "^7.3.7",
29
+ "@mui/x-charts-vendor": "^8.26.0",
29
30
  "canvas2svg": "^1.0.16",
30
31
  "copy-to-clipboard": "^3.3.3",
31
32
  "fast-deep-equal": "^3.1.3",
@@ -33,10 +34,10 @@
33
34
  "mobx": "^6.15.0",
34
35
  "mobx-react": "^9.2.1",
35
36
  "rxjs": "^7.8.2",
36
- "@jbrowse/core": "^4.0.4",
37
- "@jbrowse/plugin-linear-genome-view": "^4.0.4",
38
- "@jbrowse/plugin-wiggle": "^4.0.4",
39
- "@jbrowse/sv-core": "^4.0.4"
37
+ "@jbrowse/core": "^4.1.1",
38
+ "@jbrowse/plugin-linear-genome-view": "^4.1.1",
39
+ "@jbrowse/plugin-wiggle": "^4.1.1",
40
+ "@jbrowse/sv-core": "^4.1.1"
40
41
  },
41
42
  "peerDependencies": {
42
43
  "react": ">=18.0.0"