@jbrowse/plugin-linear-genome-view 5.0.0-beta.16 → 5.0.0-beta.17
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.
- package/esm/LinearGenomeView/axisConfigSchema.d.ts +18 -0
- package/esm/LinearGenomeView/axisConfigSchema.js +22 -0
- package/esm/LinearGenomeView/components/Highlight.js +21 -5
- package/esm/LinearGenomeView/components/HighlightBand.js +18 -13
- package/esm/LinearGenomeView/components/HighlightChip.js +9 -4
- package/esm/LinearGenomeView/components/HighlightLabelDialog.d.ts +6 -0
- package/esm/LinearGenomeView/components/HighlightLabelDialog.js +18 -0
- package/esm/LinearGenomeView/components/SVGHighlightBand.js +3 -3
- package/esm/LinearGenomeView/components/TracksContainer.js +2 -2
- package/esm/LinearGenomeView/components/useSideScroll.d.ts +3 -2
- package/esm/LinearGenomeView/components/useSideScroll.js +13 -8
- package/esm/LinearGenomeView/launchKeys.d.ts +1 -1
- package/esm/LinearGenomeView/launchKeys.js +6 -5
- package/esm/LinearGenomeView/menuItems.js +4 -4
- package/esm/LinearGenomeView/model.d.ts +37 -15
- package/esm/LinearGenomeView/model.js +28 -17
- package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +2 -2
- package/package.json +6 -6
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #config LinearGenomeViewAxis
|
|
3
|
+
* #category view
|
|
4
|
+
* The linear genome view's `axis`: the ruler along its top, whose ticks
|
|
5
|
+
* `grid` carries down through the tracks.
|
|
6
|
+
*
|
|
7
|
+
* #example
|
|
8
|
+
* ```js
|
|
9
|
+
* { type: 'LinearGenomeView', axis: { grid: false } }
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare const linearGenomeViewAxisConfigSchema: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
13
|
+
readonly grid: {
|
|
14
|
+
type: "boolean";
|
|
15
|
+
} & {
|
|
16
|
+
defaultValue: boolean;
|
|
17
|
+
};
|
|
18
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ConfigurationSchema } from '@jbrowse/core/configuration';
|
|
2
|
+
/**
|
|
3
|
+
* #config LinearGenomeViewAxis
|
|
4
|
+
* #category view
|
|
5
|
+
* The linear genome view's `axis`: the ruler along its top, whose ticks
|
|
6
|
+
* `grid` carries down through the tracks.
|
|
7
|
+
*
|
|
8
|
+
* #example
|
|
9
|
+
* ```js
|
|
10
|
+
* { type: 'LinearGenomeView', axis: { grid: false } }
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export const linearGenomeViewAxisConfigSchema = ConfigurationSchema('LinearGenomeViewAxis', {
|
|
14
|
+
/**
|
|
15
|
+
* #slot
|
|
16
|
+
*/
|
|
17
|
+
grid: {
|
|
18
|
+
type: 'boolean',
|
|
19
|
+
defaultValue: true,
|
|
20
|
+
description: "carry the ruler's ticks down through the tracks",
|
|
21
|
+
},
|
|
22
|
+
}, { closed: true });
|
|
@@ -2,14 +2,13 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useStyleTheme } from '@jbrowse/core/ui/PaletteContext';
|
|
3
3
|
import { getSession } from '@jbrowse/core/util';
|
|
4
4
|
import CloseIcon from '@mui/icons-material/Close';
|
|
5
|
+
import EditIcon from '@mui/icons-material/Edit';
|
|
5
6
|
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
|
6
7
|
import { observer } from 'mobx-react';
|
|
7
8
|
import HighlightBand from './HighlightBand.js';
|
|
8
9
|
import HighlightChip from './HighlightChip.js';
|
|
10
|
+
import HighlightLabelDialog from './HighlightLabelDialog.js';
|
|
9
11
|
import { getHighlightColor } from './util.js';
|
|
10
|
-
// A band narrower than the link-icon chip clips it into a smudge, so below this
|
|
11
|
-
// width the chip is dropped (the band itself still draws).
|
|
12
|
-
const CHIP_MIN_WIDTH = 24;
|
|
13
12
|
const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
14
13
|
const theme = useStyleTheme();
|
|
15
14
|
const coords = model.getHighlightCoords(highlight);
|
|
@@ -17,7 +16,24 @@ const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
|
17
16
|
const label = getSession(model).highlightLabelsVisible
|
|
18
17
|
? highlight.label
|
|
19
18
|
: undefined;
|
|
20
|
-
return coords ? (_jsx(HighlightBand, { coords: coords, background: bandColor.toRgbString(), label: label, gaps: model.nonGenomicTrackBands(), children:
|
|
19
|
+
return coords ? (_jsx(HighlightBand, { coords: coords, background: bandColor.toRgbString(), label: label, gaps: model.nonGenomicTrackBands(), children: _jsx(HighlightChip, { color: bandColor, label: label, tooltip: highlight.label ?? 'Highlighted region', menuItems: [
|
|
20
|
+
{
|
|
21
|
+
label: 'Edit label...',
|
|
22
|
+
icon: EditIcon,
|
|
23
|
+
onClick: () => {
|
|
24
|
+
const session = getSession(model);
|
|
25
|
+
session.queueDialog(handleClose => [
|
|
26
|
+
HighlightLabelDialog,
|
|
27
|
+
{
|
|
28
|
+
label: highlight.label,
|
|
29
|
+
onSubmit: (label) => {
|
|
30
|
+
session.updateHighlight(highlight, { label });
|
|
31
|
+
},
|
|
32
|
+
handleClose,
|
|
33
|
+
},
|
|
34
|
+
]);
|
|
35
|
+
},
|
|
36
|
+
},
|
|
21
37
|
{
|
|
22
38
|
label: 'Dismiss highlight',
|
|
23
39
|
icon: CloseIcon,
|
|
@@ -33,6 +49,6 @@ const Highlight = observer(function Highlight({ model, highlight, }) {
|
|
|
33
49
|
},
|
|
34
50
|
},
|
|
35
51
|
...model.highlightMenuItems(highlight),
|
|
36
|
-
] })
|
|
52
|
+
] }) })) : null;
|
|
37
53
|
});
|
|
38
54
|
export default Highlight;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
3
|
import { Typography } from '@mui/material';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
@@ -16,10 +16,15 @@ const useStyles = makeStyles()(theme => ({
|
|
|
16
16
|
zIndex: 1,
|
|
17
17
|
pointerEvents: 'none',
|
|
18
18
|
},
|
|
19
|
+
// a sibling of the band, so a label wider than a narrow band runs past its
|
|
20
|
+
// right edge. No `top`: its static position is the band's top edge, where
|
|
21
|
+
// the first track's TrackLabel (zIndex 200) would otherwise cover it
|
|
19
22
|
overlay: {
|
|
20
23
|
position: 'absolute',
|
|
21
|
-
top: 0,
|
|
22
24
|
left: 3,
|
|
25
|
+
zIndex: 201,
|
|
26
|
+
pointerEvents: 'none',
|
|
27
|
+
whiteSpace: 'nowrap',
|
|
23
28
|
},
|
|
24
29
|
label: {
|
|
25
30
|
color: theme.palette.text.primary,
|
|
@@ -35,20 +40,20 @@ export function gapMask(gaps) {
|
|
|
35
40
|
return `linear-gradient(to bottom, ${stops.join(', ')})`;
|
|
36
41
|
}
|
|
37
42
|
// OverviewHighlight stays separate (different bpToPx API). A non-interactive
|
|
38
|
-
// colored band with an
|
|
39
|
-
//
|
|
40
|
-
//
|
|
43
|
+
// colored band with an optional top label; when `children` (the highlight
|
|
44
|
+
// chip) is passed it renders that instead of the plain label. Visibility is
|
|
45
|
+
// toggled from the view menu
|
|
41
46
|
const HighlightBand = observer(function HighlightBand({ coords, background, label, gaps, children, }) {
|
|
42
47
|
const { classes } = useStyles();
|
|
43
48
|
const content = children ??
|
|
44
49
|
(label ? (_jsx(Typography, { variant: "caption", className: classes.label, noWrap: true, children: label })) : null);
|
|
45
|
-
return (_jsx("div", { "data-testid": "highlight-band", className: classes.highlight, style: {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { "data-testid": "highlight-band", className: classes.highlight, style: {
|
|
51
|
+
transform: `translateX(${coords.left}px)`,
|
|
52
|
+
width: coords.width,
|
|
53
|
+
background,
|
|
54
|
+
...(gaps?.length
|
|
55
|
+
? { maskImage: gapMask(gaps), WebkitMaskImage: gapMask(gaps) }
|
|
56
|
+
: undefined),
|
|
57
|
+
} }), content ? (_jsx("div", { className: classes.overlay, style: { transform: `translateX(${coords.left}px)` }, children: content })) : null] }));
|
|
53
58
|
});
|
|
54
59
|
export default HighlightBand;
|
|
@@ -3,13 +3,18 @@ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
|
3
3
|
import LinkIcon from '@mui/icons-material/Link';
|
|
4
4
|
import { Box, Tooltip, Typography } from '@mui/material';
|
|
5
5
|
import { observer } from 'mobx-react';
|
|
6
|
-
// Interactive chip
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// Interactive chip at a highlight band's top: a link icon tinted to the band
|
|
7
|
+
// color plus an optional inline label, wrapped in a context menu. Its own
|
|
8
|
+
// background keeps it legible over the first track's label, and it sets its own
|
|
9
|
+
// pointer-events since the band is click-through.
|
|
9
10
|
const HighlightChip = observer(function HighlightChip({ color, label, tooltip, menuItems, }) {
|
|
10
11
|
// hide the icon color when the band is fully transparent ("label-only"
|
|
11
12
|
// highlight); otherwise bump to 0.8 alpha so the chip stays legible
|
|
12
13
|
const chipAlpha = color.alpha() === 0 ? 0 : 0.8;
|
|
13
|
-
return (_jsx(Box, { sx: {
|
|
14
|
+
return (_jsx(Box, { sx: {
|
|
15
|
+
pointerEvents: 'auto',
|
|
16
|
+
bgcolor: 'background.paper',
|
|
17
|
+
borderRadius: 1,
|
|
18
|
+
}, children: _jsx(CascadingMenuButton, { "data-testid": "highlight-chip", menuItems: menuItems, children: _jsx(Tooltip, { title: tooltip, arrow: true, children: _jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 0.5 }, children: [_jsx(LinkIcon, { fontSize: "small", sx: { color: color.alpha(chipAlpha).toRgbString() } }), label ? (_jsx(Typography, { variant: "caption", noWrap: true, children: label })) : null] }) }) }) }));
|
|
14
19
|
});
|
|
15
20
|
export default HighlightChip;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { SubmitDialog } from '@jbrowse/core/ui';
|
|
4
|
+
import { TextField } from '@mui/material';
|
|
5
|
+
import { observer } from 'mobx-react';
|
|
6
|
+
// Names a highlight. The label is written on the band and in every view that
|
|
7
|
+
// draws the session's highlights, such as an alignment's column or a
|
|
8
|
+
// structure's residue; an empty label leaves the highlight unnamed.
|
|
9
|
+
const HighlightLabelDialog = observer(function HighlightLabelDialog({ label, onSubmit, handleClose, }) {
|
|
10
|
+
const [val, setVal] = useState(label ?? '');
|
|
11
|
+
return (_jsx(SubmitDialog, { open: true, maxWidth: "xs", fullWidth: true, title: "Edit highlight label", onCancel: handleClose, onSubmit: () => {
|
|
12
|
+
onSubmit(val.trim() || undefined);
|
|
13
|
+
handleClose();
|
|
14
|
+
}, children: _jsx(TextField, { label: "Label", autoFocus: true, fullWidth: true, variant: "outlined", value: val, onChange: event => {
|
|
15
|
+
setVal(event.target.value);
|
|
16
|
+
} }) }));
|
|
17
|
+
});
|
|
18
|
+
export default HighlightLabelDialog;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { getFillProps } from '@jbrowse/core/util';
|
|
3
3
|
// One full-height SVG highlight band with an optional label at the top,
|
|
4
|
-
// mirroring the on-screen HighlightBand div
|
|
5
|
-
//
|
|
4
|
+
// mirroring the on-screen HighlightBand div: a label wider than its band runs
|
|
5
|
+
// past the band's right edge.
|
|
6
6
|
export default function SVGHighlightBand({ coords, height, color, label, labelColor, }) {
|
|
7
|
-
return (_jsxs(_Fragment, { children: [_jsx("rect", { x: coords.left, y: 0, width: coords.width, height: height, ...getFillProps(color) }), label
|
|
7
|
+
return (_jsxs(_Fragment, { children: [_jsx("rect", { x: coords.left, y: 0, width: coords.width, height: height, ...getFillProps(color) }), label ? (_jsx("text", { x: coords.left + 3, y: 2, fontSize: 11, dominantBaseline: "hanging", ...getFillProps(labelColor), children: label })) : null] }));
|
|
8
8
|
}
|
|
@@ -40,13 +40,13 @@ const TracksContainer = observer(function TracksContainer({ children, model, })
|
|
|
40
40
|
const { classes } = useStyles();
|
|
41
41
|
const { pluginManager } = getEnv(model);
|
|
42
42
|
const sideScroll = useSideScroll(model);
|
|
43
|
-
const { showGridlines, showCenterLine } = model;
|
|
43
|
+
const { axisGrid: showGridlines, showCenterLine } = model;
|
|
44
44
|
const ref = useRef(null);
|
|
45
45
|
// shift-drag range select over the whole tracks area. This is intentionally
|
|
46
46
|
// a separate range-select instance from the one inside <Rubberband> (the
|
|
47
47
|
// scalebar control); don't dedupe them — they cover different regions.
|
|
48
48
|
const range = useRangeSelect(ref, model, true);
|
|
49
|
-
return (_jsxs("div", { ref: ref, "data-testid": "tracksContainer", className: classes.tracksContainer,
|
|
49
|
+
return (_jsxs("div", { ref: ref, "data-testid": "tracksContainer", className: classes.tracksContainer, ...sideScroll, onMouseDown: range.mouseDown, onMouseMove: event => {
|
|
50
50
|
range.mouseMove(event);
|
|
51
51
|
}, onMouseLeave: () => {
|
|
52
52
|
range.mouseOut();
|
|
@@ -15,6 +15,7 @@ import type React from 'react';
|
|
|
15
15
|
* effect, so a release that lands before React commits still finds them.
|
|
16
16
|
*/
|
|
17
17
|
export declare function useSideScroll(model: LinearGenomeViewModel): {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
onPointerDownCapture: (event: React.PointerEvent) => void;
|
|
19
|
+
onPointerDown: (event: React.PointerEvent) => void;
|
|
20
|
+
onClickCapture: (event: React.MouseEvent) => void;
|
|
20
21
|
};
|
|
@@ -10,7 +10,7 @@ const PAN_CLICK_THRESHOLD_PX = 4;
|
|
|
10
10
|
// as `data-gesture-owner`. `data-pan-dragging` is present from press to
|
|
11
11
|
// release; `data-pan-moved` is set once the press travels past the threshold
|
|
12
12
|
// or becomes a pinch, and cleared by the next press, so it still answers for
|
|
13
|
-
// the click that follows the release: `
|
|
13
|
+
// the click that follows the release: `onClickCapture` swallows that click
|
|
14
14
|
// before any track sees it.
|
|
15
15
|
const PAN_DRAGGING_ATTR = 'data-pan-dragging';
|
|
16
16
|
const PAN_MOVED_ATTR = 'data-pan-moved';
|
|
@@ -36,16 +36,21 @@ export function useSideScroll(model) {
|
|
|
36
36
|
useEffect(() => () => {
|
|
37
37
|
gestureRef.current?.cancel();
|
|
38
38
|
}, []);
|
|
39
|
-
|
|
39
|
+
// The marker outlives the gesture that set it, so every press clears it, in
|
|
40
|
+
// the capture phase: a track that keeps its own press and stops it bubbling
|
|
41
|
+
// (multiway's lane slide, maf's drag select) would otherwise leave the last
|
|
42
|
+
// pan's marker standing to swallow its click.
|
|
43
|
+
function onPointerDownCapture(event) {
|
|
44
|
+
if (!gestureRef.current) {
|
|
45
|
+
event.currentTarget.removeAttribute(PAN_MOVED_ATTR);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function onPointerDown(event) {
|
|
40
49
|
const host = event.currentTarget;
|
|
41
50
|
if (gestureRef.current) {
|
|
42
51
|
gestureRef.current.join(event);
|
|
43
52
|
return;
|
|
44
53
|
}
|
|
45
|
-
// Cleared for EVERY press, ahead of the returns below: the marker outlives
|
|
46
|
-
// the gesture that set it, and a stale true is what the next reader of
|
|
47
|
-
// this DOM contract would inherit.
|
|
48
|
-
host.removeAttribute(PAN_MOVED_ATTR);
|
|
49
54
|
if (event.shiftKey || event.button !== 0 || !event.isPrimary) {
|
|
50
55
|
return;
|
|
51
56
|
}
|
|
@@ -165,10 +170,10 @@ export function useSideScroll(model) {
|
|
|
165
170
|
// The browser fires `click` after a release on whatever the press and the
|
|
166
171
|
// release share, which over a track is its canvas, so a pan that ended on a
|
|
167
172
|
// gene would open that gene's details.
|
|
168
|
-
function
|
|
173
|
+
function onClickCapture(event) {
|
|
169
174
|
if (event.currentTarget.hasAttribute(PAN_MOVED_ATTR)) {
|
|
170
175
|
event.stopPropagation();
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
|
-
return {
|
|
178
|
+
return { onPointerDownCapture, onPointerDown, onClickCapture };
|
|
174
179
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { InitState } from './types.ts';
|
|
2
|
-
export declare const lgvLaunchKeys: import("@jbrowse/core/util/withLaunchInput").LaunchKeyRegistration<InitState, "bpPerPx" | "offsetPx" | "showCytobandsSetting">;
|
|
2
|
+
export declare const lgvLaunchKeys: import("@jbrowse/core/util/withLaunchInput").LaunchKeyRegistration<InitState, "bpPerPx" | "colorByCDS" | "offsetPx" | "showCytobandsSetting">;
|
|
@@ -4,10 +4,11 @@ import { defineLaunchKeys } from '@jbrowse/core/util/withLaunchInput';
|
|
|
4
4
|
// key that partitions as a typo.
|
|
5
5
|
//
|
|
6
6
|
// `bpPerPx`/`offsetPx` are v4's viewport spelling from before it was stored as
|
|
7
|
-
// a window,
|
|
8
|
-
// property, so the partition would read them
|
|
9
|
-
// preProcessSnapshot still converts them, so a URL or
|
|
10
|
-
// goes on working. Share links carry all
|
|
7
|
+
// a window, `showCytobandsSetting` its cytoband toggle and `colorByCDS` its
|
|
8
|
+
// frame coloring. None is a declared property, so the partition would read them
|
|
9
|
+
// as typos; the model's own preProcessSnapshot still converts them, so a URL or
|
|
10
|
+
// saved spec naming them goes on working. Share links carry all four, so they
|
|
11
|
+
// stay.
|
|
11
12
|
//
|
|
12
13
|
// #launchKeys LinearGenomeView — the URL parameters page renders this list
|
|
13
14
|
// rather than restating it; each of these keys has its own `¶m=` section
|
|
@@ -23,5 +24,5 @@ export const lgvLaunchKeys = defineLaunchKeys()({
|
|
|
23
24
|
tracks: { kind: 'trackEntries' },
|
|
24
25
|
highlight: { kind: 'launch' },
|
|
25
26
|
}, {
|
|
26
|
-
passThrough: ['bpPerPx', 'offsetPx', 'showCytobandsSetting'],
|
|
27
|
+
passThrough: ['bpPerPx', 'offsetPx', 'showCytobandsSetting', 'colorByCDS'],
|
|
27
28
|
});
|
|
@@ -229,9 +229,9 @@ export function buildMenuItems(self) {
|
|
|
229
229
|
{
|
|
230
230
|
label: 'Guidelines',
|
|
231
231
|
type: 'checkbox',
|
|
232
|
-
checked: self.
|
|
232
|
+
checked: self.axisGrid,
|
|
233
233
|
onClick: () => {
|
|
234
|
-
self.
|
|
234
|
+
self.setAxisGrid(!self.axisGrid);
|
|
235
235
|
},
|
|
236
236
|
},
|
|
237
237
|
{
|
|
@@ -254,9 +254,9 @@ export function buildMenuItems(self) {
|
|
|
254
254
|
{
|
|
255
255
|
label: 'CDS reading frame colors',
|
|
256
256
|
type: 'checkbox',
|
|
257
|
-
checked: self.
|
|
257
|
+
checked: self.colorByFrame,
|
|
258
258
|
onClick: () => {
|
|
259
|
-
self.
|
|
259
|
+
self.setColorByFrame(!self.colorByFrame);
|
|
260
260
|
},
|
|
261
261
|
},
|
|
262
262
|
{
|
|
@@ -55,7 +55,7 @@ export interface HighlightRegion {
|
|
|
55
55
|
* views: [
|
|
56
56
|
* {
|
|
57
57
|
* type: 'LinearGenomeView',
|
|
58
|
-
*
|
|
58
|
+
* colorByFrame: true,
|
|
59
59
|
* assembly: 'hg38',
|
|
60
60
|
* loc: 'chr1:1,000,000-1,100,000',
|
|
61
61
|
* tracks: ['genes', 'alignments'],
|
|
@@ -65,7 +65,7 @@ export interface HighlightRegion {
|
|
|
65
65
|
* ```
|
|
66
66
|
* `assembly`, `loc`, `tracks`, `tracklist`, `nav` and `highlight` need
|
|
67
67
|
* on-attach resolution and are captured into the `launch` property below;
|
|
68
|
-
* `
|
|
68
|
+
* `colorByFrame`, `showAminoAcids`, `showCenterLine` and `trackLabels` are plain
|
|
69
69
|
* view props MST restores natively. Both are written the same way.
|
|
70
70
|
* At runtime the same model is driven imperatively — every property and action
|
|
71
71
|
* below is reachable on `viewState.session.views[0]`:
|
|
@@ -80,7 +80,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
80
80
|
id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
81
81
|
displayName: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
|
|
82
82
|
minimized: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
83
|
-
}, "
|
|
83
|
+
}, "axis" | "colorByFrame" | "displayedRegions" | "hideHeader" | "hideHeaderOverview" | "hideNoTracksActive" | "id" | "launch" | "legacyBpPerPx" | "scalebarOnly" | "showAminoAcids" | "showCenterLine" | "showCytobands" | "showTrackOutlines" | "trackLabels" | "trackSelectorType" | "tracks" | "type" | "windowStartBp" | "windowWidthBp"> & {
|
|
84
84
|
id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
85
85
|
type: import("@jbrowse/mobx-state-tree").IType<string | undefined, string, string>;
|
|
86
86
|
windowStartBp: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<number>, [undefined]>;
|
|
@@ -95,8 +95,14 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
95
95
|
showCenterLine: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
96
96
|
showCytobands: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
97
97
|
trackLabels: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
axis: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
99
|
+
readonly grid: {
|
|
100
|
+
type: "boolean";
|
|
101
|
+
} & {
|
|
102
|
+
defaultValue: boolean;
|
|
103
|
+
};
|
|
104
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>, [undefined]>;
|
|
105
|
+
colorByFrame: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
100
106
|
showAminoAcids: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
101
107
|
showTrackOutlines: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
102
108
|
scalebarOnly: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
@@ -184,6 +190,11 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
184
190
|
*/
|
|
185
191
|
volatileGuides: VolatileGuide[];
|
|
186
192
|
} & {
|
|
193
|
+
/**
|
|
194
|
+
* #getter
|
|
195
|
+
* `axis.grid`: whether the ruler's ticks run down through the tracks.
|
|
196
|
+
*/
|
|
197
|
+
readonly axisGrid: boolean;
|
|
187
198
|
/**
|
|
188
199
|
* #getter
|
|
189
200
|
* the launch state that still has something to apply — the gate every
|
|
@@ -857,7 +868,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
857
868
|
/**
|
|
858
869
|
* #action
|
|
859
870
|
*/
|
|
860
|
-
|
|
871
|
+
setColorByFrame(flag: boolean): void;
|
|
861
872
|
/**
|
|
862
873
|
* #action
|
|
863
874
|
*/
|
|
@@ -916,7 +927,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
916
927
|
/**
|
|
917
928
|
* #action
|
|
918
929
|
*/
|
|
919
|
-
|
|
930
|
+
setAxisGrid(grid: boolean): void;
|
|
920
931
|
/**
|
|
921
932
|
* #action
|
|
922
933
|
* set temporary vertical guides (e.g., for LD display hover)
|
|
@@ -1808,7 +1819,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
1808
1819
|
id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
1809
1820
|
displayName: import("@jbrowse/mobx-state-tree").IMaybe<import("@jbrowse/mobx-state-tree").ISimpleType<string>>;
|
|
1810
1821
|
minimized: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
1811
|
-
}, "
|
|
1822
|
+
}, "axis" | "colorByFrame" | "displayedRegions" | "hideHeader" | "hideHeaderOverview" | "hideNoTracksActive" | "id" | "launch" | "legacyBpPerPx" | "scalebarOnly" | "showAminoAcids" | "showCenterLine" | "showCytobands" | "showTrackOutlines" | "trackLabels" | "trackSelectorType" | "tracks" | "type" | "windowStartBp" | "windowWidthBp"> & {
|
|
1812
1823
|
id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
1813
1824
|
type: import("@jbrowse/mobx-state-tree").IType<string | undefined, string, string>;
|
|
1814
1825
|
windowStartBp: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<number>, [undefined]>;
|
|
@@ -1823,8 +1834,14 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
1823
1834
|
showCenterLine: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
1824
1835
|
showCytobands: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
1825
1836
|
trackLabels: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
1826
|
-
|
|
1827
|
-
|
|
1837
|
+
axis: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
1838
|
+
readonly grid: {
|
|
1839
|
+
type: "boolean";
|
|
1840
|
+
} & {
|
|
1841
|
+
defaultValue: boolean;
|
|
1842
|
+
};
|
|
1843
|
+
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>, any>>, [undefined]>;
|
|
1844
|
+
colorByFrame: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
1828
1845
|
showAminoAcids: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
1829
1846
|
showTrackOutlines: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
1830
1847
|
scalebarOnly: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<boolean>, [undefined]>;
|
|
@@ -1912,6 +1929,11 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
1912
1929
|
*/
|
|
1913
1930
|
volatileGuides: VolatileGuide[];
|
|
1914
1931
|
} & {
|
|
1932
|
+
/**
|
|
1933
|
+
* #getter
|
|
1934
|
+
* `axis.grid`: whether the ruler's ticks run down through the tracks.
|
|
1935
|
+
*/
|
|
1936
|
+
readonly axisGrid: boolean;
|
|
1915
1937
|
/**
|
|
1916
1938
|
* #getter
|
|
1917
1939
|
* the launch state that still has something to apply — the gate every
|
|
@@ -2585,7 +2607,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
2585
2607
|
/**
|
|
2586
2608
|
* #action
|
|
2587
2609
|
*/
|
|
2588
|
-
|
|
2610
|
+
setColorByFrame(flag: boolean): void;
|
|
2589
2611
|
/**
|
|
2590
2612
|
* #action
|
|
2591
2613
|
*/
|
|
@@ -2644,7 +2666,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
2644
2666
|
/**
|
|
2645
2667
|
* #action
|
|
2646
2668
|
*/
|
|
2647
|
-
|
|
2669
|
+
setAxisGrid(grid: boolean): void;
|
|
2648
2670
|
/**
|
|
2649
2671
|
* #action
|
|
2650
2672
|
* set temporary vertical guides (e.g., for LD display hover)
|
|
@@ -3532,7 +3554,7 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
3532
3554
|
} & {
|
|
3533
3555
|
afterCreate(): void;
|
|
3534
3556
|
afterAttach(): void;
|
|
3535
|
-
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>, InitState, "bpPerPx" | "offsetPx" | "showCytobandsSetting">, {
|
|
3557
|
+
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>, InitState, "bpPerPx" | "colorByCDS" | "offsetPx" | "showCytobandsSetting">, {
|
|
3536
3558
|
displayName: string | undefined;
|
|
3537
3559
|
minimized: boolean;
|
|
3538
3560
|
id: string;
|
|
@@ -3548,8 +3570,8 @@ export declare function stateModelFactory(pluginManager: PluginManager): import(
|
|
|
3548
3570
|
trackSelectorType: "hierarchical";
|
|
3549
3571
|
showCytobands: boolean;
|
|
3550
3572
|
trackLabels: string;
|
|
3551
|
-
|
|
3552
|
-
|
|
3573
|
+
axis: import("@jbrowse/mobx-state-tree").ModelSnapshotType<Record<string, any>>;
|
|
3574
|
+
colorByFrame: boolean;
|
|
3553
3575
|
showAminoAcids: boolean;
|
|
3554
3576
|
showTrackOutlines: boolean;
|
|
3555
3577
|
scalebarOnly: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canonicalLocString } from '@jbrowse/core/TextSearch/places';
|
|
2
|
-
import { getConf } from '@jbrowse/core/configuration';
|
|
2
|
+
import { getConf, readConfObject } from '@jbrowse/core/configuration';
|
|
3
3
|
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models';
|
|
4
4
|
import { exportViewSvg } from '@jbrowse/core/svg/exportViewSvg';
|
|
5
5
|
import { assembleLocString, clamp, getBpDisplayStr, getNotificationSink, getSession, isSessionModelWithWidgets, localStorageGetBoolean, localStorageGetItem, localStorageSetBoolean, localStorageSetItem, springAnimate, sum, } from '@jbrowse/core/util';
|
|
@@ -15,10 +15,11 @@ import { assemblyErrorMessage, computeViewStatus, viewLoading, } from '@jbrowse/
|
|
|
15
15
|
import { pendingLaunch, withLaunchInput, } from '@jbrowse/core/util/withLaunchInput';
|
|
16
16
|
import { MIN_DISPLAY_HEIGHT } from '@jbrowse/display-kit/const';
|
|
17
17
|
import { contentRightEdgePx } from '@jbrowse/display-kit/regionHost';
|
|
18
|
-
import { cast, getParent, hasParent, isAlive, types, } from '@jbrowse/mobx-state-tree';
|
|
18
|
+
import { cast, getParent, getSnapshot, hasParent, isAlive, types, } from '@jbrowse/mobx-state-tree';
|
|
19
19
|
import { observable, when } from 'mobx';
|
|
20
20
|
import { handleSelectedRegion, isOpenInView, navToOption, } from '../searchUtils.js';
|
|
21
21
|
import { doAfterAttach } from './afterAttach.js';
|
|
22
|
+
import { linearGenomeViewAxisConfigSchema } from './axisConfigSchema.js';
|
|
22
23
|
import { getCytobands, shouldSwapTracks } from './components/util.js';
|
|
23
24
|
import { HEADER_BAR_HEIGHT, HEADER_OVERVIEW_HEIGHT, MIN_BP_PER_PX, MINIMIZED_TRACK_HEIGHT, MINIMUM_BLOCK_WIDTH, RESIZE_ALL_HANDLE_HEIGHT, RESIZE_HANDLE_HEIGHT, SCALE_BAR_HEIGHT, SHOW_ALL_REGIONS_FILL, TRACK_OUTLINE_BORDER, TRACK_TOP_GAP, } from './consts.js';
|
|
24
25
|
import { planFlight } from './flyTo.js';
|
|
@@ -154,7 +155,7 @@ function isTrackSelectorVisible(self) {
|
|
|
154
155
|
* views: [
|
|
155
156
|
* {
|
|
156
157
|
* type: 'LinearGenomeView',
|
|
157
|
-
*
|
|
158
|
+
* colorByFrame: true,
|
|
158
159
|
* assembly: 'hg38',
|
|
159
160
|
* loc: 'chr1:1,000,000-1,100,000',
|
|
160
161
|
* tracks: ['genes', 'alignments'],
|
|
@@ -164,7 +165,7 @@ function isTrackSelectorVisible(self) {
|
|
|
164
165
|
* ```
|
|
165
166
|
* `assembly`, `loc`, `tracks`, `tracklist`, `nav` and `highlight` need
|
|
166
167
|
* on-attach resolution and are captured into the `launch` property below;
|
|
167
|
-
* `
|
|
168
|
+
* `colorByFrame`, `showAminoAcids`, `showCenterLine` and `trackLabels` are plain
|
|
168
169
|
* view props MST restores natively. Both are written the same way.
|
|
169
170
|
* At runtime the same model is driven imperatively — every property and action
|
|
170
171
|
* below is reachable on `viewState.session.views[0]`:
|
|
@@ -267,7 +268,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
267
268
|
/**
|
|
268
269
|
* #property
|
|
269
270
|
* show the "center line". A personal preference: never in a session
|
|
270
|
-
* snapshot. It, showCytobands, trackLabels,
|
|
271
|
+
* snapshot. It, showCytobands, trackLabels, colorByFrame, showAminoAcids
|
|
271
272
|
* and showTrackOutlines start from this browser's last choice, which
|
|
272
273
|
* their setters record.
|
|
273
274
|
*/
|
|
@@ -291,20 +292,21 @@ export function stateModelFactory(pluginManager) {
|
|
|
291
292
|
trackLabels: types.optional(types.string, () => localStorageGetItem('lgv-trackLabels') ?? ''),
|
|
292
293
|
/**
|
|
293
294
|
* #property
|
|
294
|
-
*
|
|
295
|
+
* The ruler's guide, a [](/docs/config/lineargenomeviewaxis) object:
|
|
296
|
+
* `{ grid: false }` stops its ticks running down through the tracks.
|
|
295
297
|
*/
|
|
296
|
-
|
|
298
|
+
axis: types.stripDefault(linearGenomeViewAxisConfigSchema, {}),
|
|
297
299
|
/**
|
|
298
300
|
* #property
|
|
299
301
|
* color CDS segments by reading frame
|
|
300
302
|
*/
|
|
301
|
-
|
|
303
|
+
colorByFrame: types.optional(types.boolean, () => localStorageGetBoolean('lgv-colorByFrame', false)),
|
|
302
304
|
/**
|
|
303
305
|
* #property
|
|
304
306
|
* draw translated codons on coding features once zoomed in far enough:
|
|
305
307
|
* an alternating per-codon shading, and the amino acid letters on top of
|
|
306
|
-
* it at base-level zoom. Independent of `
|
|
307
|
-
* the segments by frame.
|
|
308
|
+
* it at base-level zoom. Independent of `colorByFrame`, which only
|
|
309
|
+
* recolors the segments by frame.
|
|
308
310
|
*/
|
|
309
311
|
showAminoAcids: types.optional(types.boolean, () => localStorageGetBoolean('lgv-showAminoAcids', true)),
|
|
310
312
|
/**
|
|
@@ -406,6 +408,13 @@ export function stateModelFactory(pluginManager) {
|
|
|
406
408
|
};
|
|
407
409
|
})
|
|
408
410
|
.views(self => ({
|
|
411
|
+
/**
|
|
412
|
+
* #getter
|
|
413
|
+
* `axis.grid`: whether the ruler's ticks run down through the tracks.
|
|
414
|
+
*/
|
|
415
|
+
get axisGrid() {
|
|
416
|
+
return readConfObject(self.axis, 'grid');
|
|
417
|
+
},
|
|
409
418
|
// The viewport in pixels, derived from the window it frames — declared
|
|
410
419
|
// ahead of every other getter so that the ~300 reads of `self.bpPerPx` /
|
|
411
420
|
// `self.offsetPx` across the codebase go on resolving by name. Only the
|
|
@@ -1053,9 +1062,9 @@ export function stateModelFactory(pluginManager) {
|
|
|
1053
1062
|
/**
|
|
1054
1063
|
* #action
|
|
1055
1064
|
*/
|
|
1056
|
-
|
|
1057
|
-
self.
|
|
1058
|
-
localStorageSetBoolean('lgv-
|
|
1065
|
+
setColorByFrame(flag) {
|
|
1066
|
+
self.colorByFrame = flag;
|
|
1067
|
+
localStorageSetBoolean('lgv-colorByFrame', flag);
|
|
1059
1068
|
},
|
|
1060
1069
|
/**
|
|
1061
1070
|
* #action
|
|
@@ -1171,8 +1180,8 @@ export function stateModelFactory(pluginManager) {
|
|
|
1171
1180
|
/**
|
|
1172
1181
|
* #action
|
|
1173
1182
|
*/
|
|
1174
|
-
|
|
1175
|
-
self.
|
|
1183
|
+
setAxisGrid(grid) {
|
|
1184
|
+
self.axis = cast({ ...getSnapshot(self.axis), grid });
|
|
1176
1185
|
},
|
|
1177
1186
|
/**
|
|
1178
1187
|
* #action
|
|
@@ -2891,12 +2900,14 @@ export function stateModelFactory(pluginManager) {
|
|
|
2891
2900
|
return snap;
|
|
2892
2901
|
}
|
|
2893
2902
|
// v4's `showCytobandsSetting` persists as the bare `showCytobands` prop
|
|
2894
|
-
// (the capability-gated getter is `effectiveShowCytobands`)
|
|
2895
|
-
|
|
2903
|
+
// (the capability-gated getter is `effectiveShowCytobands`), and v4's
|
|
2904
|
+
// `colorByCDS` as `colorByFrame`.
|
|
2905
|
+
const { showCytobandsSetting, colorByCDS, offsetPx, bpPerPx, ...rest } = snap;
|
|
2896
2906
|
return {
|
|
2897
2907
|
...(showCytobandsSetting !== undefined
|
|
2898
2908
|
? { showCytobands: showCytobandsSetting }
|
|
2899
2909
|
: {}),
|
|
2910
|
+
...(colorByCDS !== undefined ? { colorByFrame: colorByCDS } : {}),
|
|
2900
2911
|
// The viewport used to persist as pixels. Half of that converts
|
|
2901
2912
|
// exactly and needs no width — the left edge in bp is offsetPx *
|
|
2902
2913
|
// bpPerPx — and half of it cannot convert at all, because the width
|
|
@@ -126,7 +126,7 @@ const FrozenSvgFigure = memo(function FrozenSvgFigure({ view, snapshot, }) {
|
|
|
126
126
|
* (hidden sections, collapses, height overrides: `groupStateKey`), which are
|
|
127
127
|
* volatile and in no snapshot; **the reload counter**, since a Retry or a Force
|
|
128
128
|
* load leaves every snapshot as it was while the figure has an error or a
|
|
129
|
-
* too-large note to replace; **the view's coloring toggles** (`
|
|
129
|
+
* too-large note to replace; **the view's coloring toggles** (`colorByFrame`,
|
|
130
130
|
* `showAminoAcids`), named one by one because the view's own snapshot moves
|
|
131
131
|
* every pan frame; **the width**, since a resize can widen the canvas without
|
|
132
132
|
* moving the locus; **the theme**, since each display bakes colors into its own
|
|
@@ -172,7 +172,7 @@ function figureKey(view, themeName) {
|
|
|
172
172
|
display?.reloadCounter,
|
|
173
173
|
];
|
|
174
174
|
}),
|
|
175
|
-
view.
|
|
175
|
+
view.colorByFrame,
|
|
176
176
|
view.showAminoAcids,
|
|
177
177
|
session.getActiveThemeOptions?.(themeName),
|
|
178
178
|
session.highlightsVisible ? view.highlights : false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jbrowse/plugin-linear-genome-view",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "JBrowse 2 linear genome view",
|
|
6
6
|
"keywords": [
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"@mui/material": "^9.5.0",
|
|
27
27
|
"mobx": "^7.0.6",
|
|
28
28
|
"mobx-react": "^10.0.2",
|
|
29
|
-
"@jbrowse/core": "^5.0.0-beta.
|
|
30
|
-
"@jbrowse/display-
|
|
31
|
-
"@jbrowse/
|
|
32
|
-
"@jbrowse/
|
|
29
|
+
"@jbrowse/core": "^5.0.0-beta.17",
|
|
30
|
+
"@jbrowse/display-ui": "^5.0.0-beta.17",
|
|
31
|
+
"@jbrowse/render-core": "^5.0.0-beta.17",
|
|
32
|
+
"@jbrowse/display-kit": "^5.0.0-beta.17"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": ">=19.0.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"sideEffects": false,
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@jbrowse/display-test-utils": "^5.0.0-beta.
|
|
43
|
+
"@jbrowse/display-test-utils": "^5.0.0-beta.17"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"prebuild": "pnpm clean",
|