@jbrowse/plugin-linear-genome-view 5.0.0-beta.17 → 5.0.0-beta.19
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/afterAttach.js +6 -4
- package/esm/LinearGenomeView/components/ImportForm.js +2 -4
- package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +4 -2
- package/esm/LinearGenomeView/components/SearchBox.js +1 -9
- package/esm/LinearGenomeView/components/SearchResultsDialog.js +1 -1
- package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +2 -1
- package/esm/LinearGenomeView/components/SearchResultsTable.js +23 -5
- package/esm/LinearGenomeView/components/SequenceFeatureHoverHighlight.js +2 -3
- package/esm/LinearGenomeView/components/TrackContainer.d.ts +2 -3
- package/esm/LinearGenomeView/components/TrackContainer.js +3 -4
- package/esm/LinearGenomeView/components/TrackLabelRefNameWarning.d.ts +3 -4
- package/esm/LinearGenomeView/components/TrackLabelRefNameWarning.js +23 -10
- package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +2 -3
- package/esm/LinearGenomeView/components/TrackRenderingContainer.js +1 -1
- package/esm/LinearGenomeView/components/useSearchBoxPrefs.d.ts +1 -1
- package/esm/LinearGenomeView/components/useSearchBoxPrefs.js +1 -1
- package/esm/LinearGenomeView/index.d.ts +1 -1
- package/esm/LinearGenomeView/index.js +2 -0
- package/esm/LinearGenomeView/keyboardHandler.d.ts +8 -9
- package/esm/LinearGenomeView/keyboardHandler.js +58 -34
- package/esm/LinearGenomeView/menuItems.js +3 -2
- package/esm/LinearGenomeView/model.d.ts +489 -52
- package/esm/LinearGenomeView/model.js +125 -45
- package/esm/LinearGenomeView/rExport/exportLinearView.js +4 -5
- package/esm/LinearGenomeView/rExport/rScript.d.ts +26 -1
- package/esm/LinearGenomeView/rExport/rScript.js +27 -6
- package/esm/LinearGenomeView/sharedScaleContainer.js +2 -12
- package/esm/LinearGenomeView/showFiles.d.ts +2 -0
- package/esm/LinearGenomeView/showFiles.js +19 -0
- package/esm/LinearGenomeView/showRegionsWithUndo.d.ts +1 -1
- package/esm/LinearGenomeView/showRegionsWithUndo.js +1 -1
- package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +3 -1
- package/esm/LinearGenomeView/util.d.ts +1 -1
- package/esm/LinearGenomeView/util.js +1 -1
- package/esm/TrackModifiers/index.d.ts +80 -0
- package/esm/TrackModifiers/index.js +212 -0
- package/esm/displayKitTests/perRegionTestEnv.d.ts +10 -4
- package/esm/index.d.ts +3 -1
- package/esm/index.js +1 -2
- package/esm/searchUtils.d.ts +14 -2
- package/esm/searchUtils.js +56 -36
- package/package.json +10 -9
- package/esm/FeatureTrack/configSchema.d.ts +0 -217
- package/esm/FeatureTrack/configSchema.js +0 -55
- package/esm/FeatureTrack/index.d.ts +0 -2
- package/esm/FeatureTrack/index.js +0 -13
|
@@ -2,12 +2,14 @@ import { canonicalLocString } from '@jbrowse/core/TextSearch/places';
|
|
|
2
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
|
-
import {
|
|
5
|
+
import { highlightListMenuItems } from '@jbrowse/core/ui/menuItems';
|
|
6
|
+
import { assembleLocString, clamp, getBpDisplayStr, getEnv, getNotificationSink, getSession, isSessionModelWithWidgets, localStorageGetBoolean, localStorageGetItem, localStorageSetBoolean, localStorageSetItem, springAnimate, sum, } from '@jbrowse/core/util';
|
|
6
7
|
import { bpToLinearBp, bpToPx, computeMoveToLayout, createOverviewLayout, getLayoutHighlightCoords, getOverviewRegionPxSpan, moveTo, pxToBp, } from '@jbrowse/core/util/Base1DUtils';
|
|
7
8
|
import { scaleBandHeights } from '@jbrowse/core/util/bandHeight';
|
|
8
9
|
import { wholeBaseRegions } from '@jbrowse/core/util/blockTypes';
|
|
9
10
|
import calculateDynamicBlocks from '@jbrowse/core/util/calculateDynamicBlocks';
|
|
10
11
|
import calculateStaticBlocks from '@jbrowse/core/util/calculateStaticBlocks';
|
|
12
|
+
import { coerceHighlight } from '@jbrowse/core/util/highlights';
|
|
11
13
|
import { hideTrackGeneric, launchToggleTrackGeneric, launchTrackGeneric, showTrackGeneric, toggleTrackGeneric, } from '@jbrowse/core/util/tracks';
|
|
12
14
|
import { ElementId } from '@jbrowse/core/util/types/mst';
|
|
13
15
|
import { highlightsOnAssemblies } from '@jbrowse/core/util/viewHighlights';
|
|
@@ -17,7 +19,7 @@ import { MIN_DISPLAY_HEIGHT } from '@jbrowse/display-kit/const';
|
|
|
17
19
|
import { contentRightEdgePx } from '@jbrowse/display-kit/regionHost';
|
|
18
20
|
import { cast, getParent, getSnapshot, hasParent, isAlive, types, } from '@jbrowse/mobx-state-tree';
|
|
19
21
|
import { observable, when } from 'mobx';
|
|
20
|
-
import { handleSelectedRegion, isOpenInView, navToOption, } from '../searchUtils.js';
|
|
22
|
+
import { fetchResults, handleSelectedRegion, isOpenInView, navToOption, } from '../searchUtils.js';
|
|
21
23
|
import { doAfterAttach } from './afterAttach.js';
|
|
22
24
|
import { linearGenomeViewAxisConfigSchema } from './axisConfigSchema.js';
|
|
23
25
|
import { getCytobands, shouldSwapTracks } from './components/util.js';
|
|
@@ -27,7 +29,43 @@ import { setupKeyboardHandler } from './keyboardHandler.js';
|
|
|
27
29
|
import { lgvLaunchKeys } from './launchKeys.js';
|
|
28
30
|
import { buildHighlightsSubMenuItems, buildMenuItems, buildRubberBandMenuItems, buildRubberbandClickMenuItems, } from './menuItems.js';
|
|
29
31
|
import { sharedScaleContainerOf } from './sharedScaleContainer.js';
|
|
32
|
+
import { showFiles } from './showFiles.js';
|
|
30
33
|
import { calculateVisibleLocStrings, cytobandLabelGutterWidth, expandRegion, fitAllRegionsWindow, generateLocations, getGridlineTicks, getPaddingSpans, getScalebarLabels, getScalebarRefNameLabels, regionsOrientation, setDisplayedRegionsKeepingCenter, } from './util.js';
|
|
34
|
+
function searchHitsOf(model, results, query, assemblyName, land) {
|
|
35
|
+
const session = getSession(model);
|
|
36
|
+
const assembly = session.assemblyManager.get(assemblyName);
|
|
37
|
+
const ordered = [...results].sort((a, b) => Number(isOpenInView(b, model)) - Number(isOpenInView(a, model)));
|
|
38
|
+
return {
|
|
39
|
+
query,
|
|
40
|
+
assemblyName,
|
|
41
|
+
rows: ordered.map(result => {
|
|
42
|
+
const location = result.getLocation() ?? '';
|
|
43
|
+
const trackId = result.getTrackId();
|
|
44
|
+
return {
|
|
45
|
+
id: result.getId(),
|
|
46
|
+
label: result.getLabel(),
|
|
47
|
+
location: assembly && location
|
|
48
|
+
? canonicalLocString(location, assembly)
|
|
49
|
+
: location,
|
|
50
|
+
trackName: trackId ? (session.getTrackById(trackId)?.name ?? '') : '',
|
|
51
|
+
};
|
|
52
|
+
}),
|
|
53
|
+
pick: async (id) => {
|
|
54
|
+
const picked = ordered.find(result => result.getId() === id);
|
|
55
|
+
try {
|
|
56
|
+
if (picked) {
|
|
57
|
+
await land(picked);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
console.error(e);
|
|
62
|
+
if (isAlive(model)) {
|
|
63
|
+
getNotificationSink(model).notifyError(`${e}`, e);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
31
69
|
/**
|
|
32
70
|
* Calculate the offsetPx needed to center content within a viewport.
|
|
33
71
|
* Returns a negative offset when content is smaller than viewport (padding on left).
|
|
@@ -260,11 +298,11 @@ export function stateModelFactory(pluginManager) {
|
|
|
260
298
|
hideNoTracksActive: types.stripDefault(types.boolean, false),
|
|
261
299
|
/**
|
|
262
300
|
* #property
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
301
|
+
* ignored: the hierarchical selector is the only one. Declared because
|
|
302
|
+
* saved sessions and share links carry it.
|
|
303
|
+
* @deprecated
|
|
266
304
|
*/
|
|
267
|
-
trackSelectorType: types.stripDefault(types.
|
|
305
|
+
trackSelectorType: types.stripDefault(types.string, 'hierarchical'),
|
|
268
306
|
/**
|
|
269
307
|
* #property
|
|
270
308
|
* show the "center line". A personal preference: never in a session
|
|
@@ -388,6 +426,13 @@ export function stateModelFactory(pluginManager) {
|
|
|
388
426
|
// displays that lay out against it; `setWidth` seeds the true one at
|
|
389
427
|
// the first measure.
|
|
390
428
|
coarseBpPerPx: 1,
|
|
429
|
+
/**
|
|
430
|
+
* #volatile
|
|
431
|
+
* the scroll offset the coarse blocks were laid out at, in px at
|
|
432
|
+
* `coarseBpPerPx`: the settled viewport's left edge, which the first
|
|
433
|
+
* coarse block is not while the view is scrolled before the first region
|
|
434
|
+
*/
|
|
435
|
+
coarseOffsetPx: 0,
|
|
391
436
|
/**
|
|
392
437
|
* #volatile
|
|
393
438
|
*/
|
|
@@ -1139,6 +1184,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
1139
1184
|
// The debounced coarse autorun is 500ms out; a restored snapshot's
|
|
1140
1185
|
// consumers would lay out and fetch at 1 bp/px until then.
|
|
1141
1186
|
self.coarseBpPerPx = self.bpPerPx;
|
|
1187
|
+
self.coarseOffsetPx = self.offsetPx;
|
|
1142
1188
|
}
|
|
1143
1189
|
},
|
|
1144
1190
|
/**
|
|
@@ -1264,40 +1310,11 @@ export function stateModelFactory(pluginManager) {
|
|
|
1264
1310
|
model: self,
|
|
1265
1311
|
assemblyName,
|
|
1266
1312
|
})) {
|
|
1267
|
-
const
|
|
1268
|
-
const assembly = session.assemblyManager.get(assemblyName);
|
|
1269
|
-
const ordered = [...searchResults].sort((a, b) => Number(isOpenInView(b, self)) -
|
|
1270
|
-
Number(isOpenInView(a, self)));
|
|
1313
|
+
const hits = searchHitsOf(self, searchResults, searchQuery, assemblyName, onPick);
|
|
1271
1314
|
const picker = {
|
|
1272
|
-
|
|
1273
|
-
assemblyName,
|
|
1274
|
-
rows: ordered.map(result => {
|
|
1275
|
-
const location = result.getLocation() ?? '';
|
|
1276
|
-
const trackId = result.getTrackId();
|
|
1277
|
-
return {
|
|
1278
|
-
id: result.getId(),
|
|
1279
|
-
label: result.getLabel(),
|
|
1280
|
-
location: assembly && location
|
|
1281
|
-
? canonicalLocString(location, assembly)
|
|
1282
|
-
: location,
|
|
1283
|
-
trackName: trackId
|
|
1284
|
-
? (session.getTrackById(trackId)?.name ?? '')
|
|
1285
|
-
: '',
|
|
1286
|
-
};
|
|
1287
|
-
}),
|
|
1315
|
+
...hits,
|
|
1288
1316
|
pick: async (id) => {
|
|
1289
|
-
|
|
1290
|
-
try {
|
|
1291
|
-
if (picked) {
|
|
1292
|
-
await onPick(picked);
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
catch (e) {
|
|
1296
|
-
console.error(e);
|
|
1297
|
-
if (isAlive(self)) {
|
|
1298
|
-
getNotificationSink(self).notifyError(`${e}`, e);
|
|
1299
|
-
}
|
|
1300
|
-
}
|
|
1317
|
+
await hits.pick(id);
|
|
1301
1318
|
if (isAlive(self) && self.searchPicker === picker) {
|
|
1302
1319
|
self.closeSearchPicker();
|
|
1303
1320
|
}
|
|
@@ -1305,6 +1322,29 @@ export function stateModelFactory(pluginManager) {
|
|
|
1305
1322
|
};
|
|
1306
1323
|
self.searchPicker = picker;
|
|
1307
1324
|
},
|
|
1325
|
+
/**
|
|
1326
|
+
* #action
|
|
1327
|
+
* Search the view's assembly by name, for a host listing the hits
|
|
1328
|
+
* itself. A row's `pick` lands as the search box does: it navigates
|
|
1329
|
+
* there and shows the track the hit's index came from.
|
|
1330
|
+
*/
|
|
1331
|
+
async search(query, opts) {
|
|
1332
|
+
const assemblyName = self.assemblyNames[0] ?? self.launchAssemblyName;
|
|
1333
|
+
if (!assemblyName) {
|
|
1334
|
+
throw new Error('the view has no assembly to search yet');
|
|
1335
|
+
}
|
|
1336
|
+
const results = await fetchResults({
|
|
1337
|
+
session: getSession(self),
|
|
1338
|
+
queryString: query,
|
|
1339
|
+
assemblyName,
|
|
1340
|
+
signal: opts?.signal,
|
|
1341
|
+
});
|
|
1342
|
+
return searchHitsOf(self, results, query, assemblyName, result => navToOption({
|
|
1343
|
+
option: result,
|
|
1344
|
+
model: self,
|
|
1345
|
+
assemblyName,
|
|
1346
|
+
}));
|
|
1347
|
+
},
|
|
1308
1348
|
/**
|
|
1309
1349
|
* #action
|
|
1310
1350
|
*/
|
|
@@ -1337,6 +1377,34 @@ export function stateModelFactory(pluginManager) {
|
|
|
1337
1377
|
async launchTrack(trackId, initialSnapshot = {}, displayInitialSnapshot = {}, inlineConf) {
|
|
1338
1378
|
return launchTrackGeneric(self, trackId, initialSnapshot, displayInitialSnapshot, inlineConf);
|
|
1339
1379
|
},
|
|
1380
|
+
/**
|
|
1381
|
+
* #action
|
|
1382
|
+
* Replace the highlights `highlights` reads, each written as the launch
|
|
1383
|
+
* spec's `highlight` writes one: a locstring, or an object whose
|
|
1384
|
+
* `assemblyName` defaults to the view's. Throws on one that names no
|
|
1385
|
+
* region, writing none.
|
|
1386
|
+
*/
|
|
1387
|
+
setHighlights(highlights) {
|
|
1388
|
+
const session = getSession(self);
|
|
1389
|
+
const assemblyName = self.assemblyNames[0] ?? self.launchAssemblyName ?? '';
|
|
1390
|
+
session.setHighlights(highlights.map(h => {
|
|
1391
|
+
const highlight = coerceHighlight(h, assemblyName, (ref, asm) => session.assemblyManager.isValidRefName(ref, asm ?? assemblyName));
|
|
1392
|
+
if (!highlight) {
|
|
1393
|
+
throw new Error(`not a region to highlight: ${JSON.stringify(h)}`);
|
|
1394
|
+
}
|
|
1395
|
+
return highlight;
|
|
1396
|
+
}));
|
|
1397
|
+
},
|
|
1398
|
+
/**
|
|
1399
|
+
* #action
|
|
1400
|
+
* Show files from the user's disk as session tracks: one per data file,
|
|
1401
|
+
* beside the index among them that names it, its type guessed from its
|
|
1402
|
+
* name. Adds nothing when one file has no known type. Resolves the new
|
|
1403
|
+
* tracks' ids.
|
|
1404
|
+
*/
|
|
1405
|
+
async showFiles(files) {
|
|
1406
|
+
return showFiles(self, files);
|
|
1407
|
+
},
|
|
1340
1408
|
}))
|
|
1341
1409
|
.views(self => ({
|
|
1342
1410
|
/**
|
|
@@ -2167,6 +2235,9 @@ export function stateModelFactory(pluginManager) {
|
|
|
2167
2235
|
self.coarseTotalBp = blocks.totalBp;
|
|
2168
2236
|
self.coarseBpPerPx = bpPerPx;
|
|
2169
2237
|
}
|
|
2238
|
+
// on every call, since a scroll through the padding before the first
|
|
2239
|
+
// region moves the viewport and leaves the blocks the same
|
|
2240
|
+
self.coarseOffsetPx = self.offsetPx;
|
|
2170
2241
|
},
|
|
2171
2242
|
}))
|
|
2172
2243
|
.actions(self => ({
|
|
@@ -2177,7 +2248,7 @@ export function stateModelFactory(pluginManager) {
|
|
|
2177
2248
|
*
|
|
2178
2249
|
* The coarse blocks are a 500ms throttle, and every consumer trades
|
|
2179
2250
|
* freshness for not recomputing per animation frame — the location box,
|
|
2180
|
-
*
|
|
2251
|
+
* the feature display's on-screen feature set, and the two per-bp scans behind
|
|
2181
2252
|
* `settledDynamicBlocks`. That trade is only sound while the answer is a
|
|
2182
2253
|
* few frames old. **A jump makes it unrelated instead**, because there is
|
|
2183
2254
|
* nothing to coalesce: the viewport left behind is not an approximation of
|
|
@@ -2638,16 +2709,26 @@ export function stateModelFactory(pluginManager) {
|
|
|
2638
2709
|
getSession(self).addHighlight(region);
|
|
2639
2710
|
}
|
|
2640
2711
|
},
|
|
2712
|
+
/**
|
|
2713
|
+
* #action
|
|
2714
|
+
* navigates to a highlight, zoomed out a little for context. Goes through
|
|
2715
|
+
* a locstring so it reaches a refName the view is not displaying
|
|
2716
|
+
*/
|
|
2717
|
+
navToHighlight(highlight) {
|
|
2718
|
+
const { assemblyName, ...region } = highlight;
|
|
2719
|
+
return self.navToLocString(assembleLocString(region), assemblyName, 0.2);
|
|
2720
|
+
},
|
|
2721
|
+
}))
|
|
2722
|
+
.actions(self => ({
|
|
2641
2723
|
/**
|
|
2642
2724
|
* #action
|
|
2643
2725
|
* navigates to the most recently added highlight on this view's
|
|
2644
|
-
* assemblies
|
|
2726
|
+
* assemblies
|
|
2645
2727
|
*/
|
|
2646
2728
|
async navigateNewestHighlight() {
|
|
2647
2729
|
const newest = self.highlights.at(-1);
|
|
2648
2730
|
if (newest) {
|
|
2649
|
-
|
|
2650
|
-
await self.navToLocString(assembleLocString(region), assemblyName, 0.2);
|
|
2731
|
+
await self.navToHighlight(newest);
|
|
2651
2732
|
}
|
|
2652
2733
|
else {
|
|
2653
2734
|
getSession(self).notify('There are no highlights to navigate to.', 'info');
|
|
@@ -2758,11 +2839,10 @@ export function stateModelFactory(pluginManager) {
|
|
|
2758
2839
|
},
|
|
2759
2840
|
/**
|
|
2760
2841
|
* #method
|
|
2761
|
-
*
|
|
2762
|
-
* this via Core-extendPluggableElement to add items
|
|
2842
|
+
* a highlight chip's menu rows after Dismiss; extend this to add one
|
|
2763
2843
|
*/
|
|
2764
2844
|
highlightMenuItems(_highlight) {
|
|
2765
|
-
return
|
|
2845
|
+
return highlightListMenuItems(getSession(self), getEnv(self).pluginManager);
|
|
2766
2846
|
},
|
|
2767
2847
|
}))
|
|
2768
2848
|
.actions(self => {
|
|
@@ -2,9 +2,6 @@ import { fullConfSnapshot, readConfObject } from '@jbrowse/core/configuration';
|
|
|
2
2
|
import { getSession } from '@jbrowse/core/util';
|
|
3
3
|
import { getSnapshot } from '@jbrowse/mobx-state-tree';
|
|
4
4
|
import { exportTracksToR } from './rScript.js';
|
|
5
|
-
function trackConf(track) {
|
|
6
|
-
return track.configuration;
|
|
7
|
-
}
|
|
8
5
|
/** The regions on screen, each displayed region cut to what the view shows of it. */
|
|
9
6
|
function viewRegions(view) {
|
|
10
7
|
const blocks = view.dynamicBlocks.contentBlocks;
|
|
@@ -52,10 +49,10 @@ function tracksOf(view) {
|
|
|
52
49
|
const { heightMode, height } = fullConfSnapshot(display.configuration);
|
|
53
50
|
tracks.push({
|
|
54
51
|
trackId: track.configuration.trackId,
|
|
55
|
-
trackName: readConfObject(
|
|
52
|
+
trackName: readConfObject(track.configuration, 'name') ||
|
|
56
53
|
track.configuration.trackId,
|
|
57
54
|
displayType: display.type,
|
|
58
|
-
adapter: readConfObject(
|
|
55
|
+
adapter: readConfObject(track.configuration, 'adapter'),
|
|
59
56
|
display: written,
|
|
60
57
|
heightMode,
|
|
61
58
|
height,
|
|
@@ -71,6 +68,8 @@ export function exportLinearViewToR(view, out) {
|
|
|
71
68
|
regions: viewRegions(view),
|
|
72
69
|
assembly: assemblyOf(view),
|
|
73
70
|
bpPerPx: view.bpPerPx,
|
|
71
|
+
colorByFrame: view.colorByFrame,
|
|
72
|
+
showAminoAcids: view.showAminoAcids,
|
|
74
73
|
out,
|
|
75
74
|
notes: view.displayedRegions.some(r => r.reversed)
|
|
76
75
|
? ['view: shown right to left, where the figure runs left to right']
|
|
@@ -15,6 +15,18 @@ export interface RExportTrack {
|
|
|
15
15
|
heightMode?: unknown;
|
|
16
16
|
height?: unknown;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Each display ggjbrowse reads, by the reader that reads it, and the adapters
|
|
20
|
+
* it opens (the slot holding the file and the extension the reader knows it
|
|
21
|
+
* by).
|
|
22
|
+
*/
|
|
23
|
+
export declare const READERS: Record<string, {
|
|
24
|
+
call: string;
|
|
25
|
+
adapters: Record<string, {
|
|
26
|
+
slot?: string;
|
|
27
|
+
ext?: RegExp;
|
|
28
|
+
}>;
|
|
29
|
+
}>;
|
|
18
30
|
/** The ggjbrowse release whose readers and `jbrowse_stack` the script calls. */
|
|
19
31
|
export declare const GGJBROWSE_VERSION = "0.10.0";
|
|
20
32
|
/**
|
|
@@ -35,6 +47,15 @@ export declare function rValue(value: unknown): string;
|
|
|
35
47
|
* `file://` URL as its path, or a `localPath`.
|
|
36
48
|
*/
|
|
37
49
|
export declare function locationPath(location: unknown): string | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* Each sequence adapter `jbrowse_assembly()` builds from a path, by the
|
|
52
|
+
* extension ggjbrowse reads it off, and the index each takes beside it.
|
|
53
|
+
*/
|
|
54
|
+
export declare const SEQUENCE_ADAPTERS: Record<string, {
|
|
55
|
+
slot: string;
|
|
56
|
+
ext: RegExp;
|
|
57
|
+
beside: Record<string, string>;
|
|
58
|
+
}>;
|
|
38
59
|
/**
|
|
39
60
|
* The view's assembly as the R value every reader takes: `jbrowse_assembly()`
|
|
40
61
|
* of its sequence and alias files where ggjbrowse builds the same config from
|
|
@@ -54,13 +75,17 @@ export declare function locusOf(refName: string, start: number, end: number): st
|
|
|
54
75
|
* header instead of stopping the figure: a script that quietly differs from
|
|
55
76
|
* its view says so.
|
|
56
77
|
*/
|
|
57
|
-
export declare function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, bpPerPx, out, notes: viewNotes, }: {
|
|
78
|
+
export declare function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, bpPerPx, colorByFrame, showAminoAcids, out, notes: viewNotes, }: {
|
|
58
79
|
tracks: readonly RExportTrack[];
|
|
59
80
|
regions: readonly Region[];
|
|
60
81
|
/** The view's assembly config as written, which names its sequence and its contigs' aliases. */
|
|
61
82
|
assembly?: Record<string, unknown>;
|
|
62
83
|
/** The view's zoom, which decides what a feature or quantitative display shows. */
|
|
63
84
|
bpPerPx?: number;
|
|
85
|
+
/** The view's "CDS reading frame colors", which a feature track takes. */
|
|
86
|
+
colorByFrame?: boolean;
|
|
87
|
+
/** The view's "Amino acids when zoomed in", which a feature track takes. */
|
|
88
|
+
showAminoAcids?: boolean;
|
|
64
89
|
out?: string;
|
|
65
90
|
/** What the caller knows of the view that the figure does not show. */
|
|
66
91
|
notes?: readonly string[];
|
|
@@ -5,7 +5,7 @@ import { resolveUri } from '@jbrowse/core/util';
|
|
|
5
5
|
* it opens (the slot holding the file and the extension the reader knows it
|
|
6
6
|
* by).
|
|
7
7
|
*/
|
|
8
|
-
const READERS = {
|
|
8
|
+
export const READERS = {
|
|
9
9
|
LinearBasicDisplay: {
|
|
10
10
|
call: 'jbrowse_features',
|
|
11
11
|
adapters: {
|
|
@@ -93,9 +93,15 @@ const RESERVED = new Set([
|
|
|
93
93
|
'Inf',
|
|
94
94
|
'NaN',
|
|
95
95
|
'NA',
|
|
96
|
+
'NA_integer_',
|
|
97
|
+
'NA_real_',
|
|
98
|
+
'NA_complex_',
|
|
99
|
+
'NA_character_',
|
|
96
100
|
]);
|
|
97
101
|
function isIdentifier(s) {
|
|
98
|
-
return /^[A-Za-z
|
|
102
|
+
return (/^([A-Za-z]|\.(?!\d))[A-Za-z0-9._]*$/.test(s) &&
|
|
103
|
+
!/^(\.\.\.|\.\.\d+)$/.test(s) &&
|
|
104
|
+
!RESERVED.has(s));
|
|
99
105
|
}
|
|
100
106
|
/**
|
|
101
107
|
* A config value as the R literal ggjbrowse hands the engine: a `list` per
|
|
@@ -216,7 +222,7 @@ function fileArgument(adapters, adapter) {
|
|
|
216
222
|
* Each sequence adapter `jbrowse_assembly()` builds from a path, by the
|
|
217
223
|
* extension ggjbrowse reads it off, and the index each takes beside it.
|
|
218
224
|
*/
|
|
219
|
-
const SEQUENCE_ADAPTERS = {
|
|
225
|
+
export const SEQUENCE_ADAPTERS = {
|
|
220
226
|
TwoBitAdapter: { slot: 'twoBitLocation', ext: /\.2bit$/, beside: {} },
|
|
221
227
|
BgzipFastaAdapter: {
|
|
222
228
|
slot: 'fastaLocation',
|
|
@@ -328,9 +334,19 @@ function trackNames(tracks) {
|
|
|
328
334
|
return name;
|
|
329
335
|
});
|
|
330
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* A track's `jbrowse_stack()` argument, named as the view labels it so its
|
|
339
|
+
* band is titled. A name `width` would set the stack's own `width`, so that
|
|
340
|
+
* one goes in a list, whose names title its tracks too.
|
|
341
|
+
*/
|
|
342
|
+
function stackArgument(title, name) {
|
|
343
|
+
return title === 'width'
|
|
344
|
+
? `list(width = ${name})`
|
|
345
|
+
: `${isIdentifier(title) ? title : rStr(title)} = ${name}`;
|
|
346
|
+
}
|
|
331
347
|
/** What the figure cannot show of a track whose settings ggjbrowse reads. */
|
|
332
348
|
function settingNotes(track) {
|
|
333
|
-
const color = track.display.
|
|
349
|
+
const { color } = track.display.encoding ?? {};
|
|
334
350
|
return [
|
|
335
351
|
...(color?.field === 'mateRefName'
|
|
336
352
|
? [
|
|
@@ -346,7 +362,7 @@ function settingNotes(track) {
|
|
|
346
362
|
* header instead of stopping the figure: a script that quietly differs from
|
|
347
363
|
* its view says so.
|
|
348
364
|
*/
|
|
349
|
-
export function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, bpPerPx, out = 'figure.png', notes: viewNotes = [], }) {
|
|
365
|
+
export function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, bpPerPx, colorByFrame = false, showAminoAcids = true, out = 'figure.png', notes: viewNotes = [], }) {
|
|
350
366
|
const notes = [...viewNotes];
|
|
351
367
|
const assembly = assemblySnapshot && assemblyArgument(assemblySnapshot);
|
|
352
368
|
if (assembly && 'skipped' in assembly) {
|
|
@@ -360,6 +376,7 @@ export function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, b
|
|
|
360
376
|
const names = trackNames(tracks);
|
|
361
377
|
const reads = [];
|
|
362
378
|
const drawn = [];
|
|
379
|
+
const bands = [];
|
|
363
380
|
let alignments;
|
|
364
381
|
tracks.forEach((track, i) => {
|
|
365
382
|
const reader = READERS[track.displayType];
|
|
@@ -381,10 +398,13 @@ export function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, b
|
|
|
381
398
|
}
|
|
382
399
|
: track.display;
|
|
383
400
|
const width = viewPx !== undefined;
|
|
401
|
+
const geneTrack = track.displayType === 'LinearBasicDisplay';
|
|
384
402
|
const args = [
|
|
385
403
|
...(Object.keys(display).length ? [`display = ${rValue(display)}`] : []),
|
|
386
404
|
...(width ? ['width = width'] : []),
|
|
387
405
|
...(assemblyValue ? ['assembly = assembly'] : []),
|
|
406
|
+
...(geneTrack && colorByFrame ? ['color_by_cds = TRUE'] : []),
|
|
407
|
+
...(geneTrack && !showAminoAcids ? ['amino_acids = FALSE'] : []),
|
|
388
408
|
...(source.index ? [`index = ${rStr(source.index)}`] : []),
|
|
389
409
|
];
|
|
390
410
|
const name = names[i];
|
|
@@ -397,6 +417,7 @@ export function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, b
|
|
|
397
417
|
` ${['locus', ...args].join(',\n ')})`,
|
|
398
418
|
].join('\n'));
|
|
399
419
|
drawn.push(name);
|
|
420
|
+
bands.push(stackArgument(track.trackName, name));
|
|
400
421
|
if (reader.call === 'jbrowse_alignments') {
|
|
401
422
|
alignments ??= name;
|
|
402
423
|
}
|
|
@@ -456,7 +477,7 @@ export function exportTracksToR({ tracks, regions, assembly: assemblySnapshot, b
|
|
|
456
477
|
'',
|
|
457
478
|
reads.join('\n\n'),
|
|
458
479
|
'',
|
|
459
|
-
`fig <- jbrowse_stack(${
|
|
480
|
+
`fig <- jbrowse_stack(${bands.join(',\n ')})`,
|
|
460
481
|
'print(fig)',
|
|
461
482
|
`ggplot2::ggsave(${rStr(out)}, fig, width = jbrowse_width(fig), height = jbrowse_height(fig))`,
|
|
462
483
|
'',
|
|
@@ -1,17 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { findParent } from '@jbrowse/mobx-state-tree';
|
|
2
2
|
export function isSharedScaleContainer(thing) {
|
|
3
3
|
return typeof thing === 'object' && thing !== null && 'sharedFit' in thing;
|
|
4
4
|
}
|
|
5
|
-
// Walks rather than using findParentThatIs because having no such container is
|
|
6
|
-
// the ordinary standalone case here, not the error that helper reports.
|
|
7
5
|
export function sharedScaleContainerOf(node) {
|
|
8
|
-
|
|
9
|
-
let found;
|
|
10
|
-
while (!found && hasParent(current) && isAlive(current)) {
|
|
11
|
-
current = getParent(current);
|
|
12
|
-
if (isSharedScaleContainer(current)) {
|
|
13
|
-
found = current;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return found;
|
|
6
|
+
return findParent(node, isSharedScaleContainer);
|
|
17
7
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { fileToLocation, getEnv, getSession, isSessionWithAddSessionTrack, } from '@jbrowse/core/util';
|
|
2
|
+
import { pairIndexFiles } from '@jbrowse/core/util/indexCandidates';
|
|
3
|
+
import { guessTrackConfForLocation } from '@jbrowse/core/util/tracks';
|
|
4
|
+
// Every type is guessed before any track is added, so a selection holding one
|
|
5
|
+
// file of no known type adds nothing rather than half of itself.
|
|
6
|
+
export async function showFiles(view, files) {
|
|
7
|
+
const session = getSession(view);
|
|
8
|
+
if (!isSessionWithAddSessionTrack(session)) {
|
|
9
|
+
throw new Error('this session does not take new tracks');
|
|
10
|
+
}
|
|
11
|
+
const { pluginManager } = getEnv(view);
|
|
12
|
+
const assemblyName = view.assemblyNames[0] ?? view.launchAssemblyName;
|
|
13
|
+
const confs = pairIndexFiles(files).map(({ data, index }) => guessTrackConfForLocation(fileToLocation(data), index && fileToLocation(index), pluginManager, assemblyName));
|
|
14
|
+
for (const conf of confs) {
|
|
15
|
+
session.addSessionTrackConf(conf);
|
|
16
|
+
}
|
|
17
|
+
await Promise.all(confs.map(conf => view.launchTrack(conf.trackId)));
|
|
18
|
+
return confs.map(conf => conf.trackId);
|
|
19
|
+
}
|
|
@@ -6,7 +6,7 @@ import type { Region } from '@jbrowse/core/util/types';
|
|
|
6
6
|
*
|
|
7
7
|
* This is the "navigate the view you are looking at" half of a launcher, as
|
|
8
8
|
* against `session.addView`, which opens a second one. Two features do it —
|
|
9
|
-
* plugin-
|
|
9
|
+
* plugin-features's collapsed introns and plugin-alignments' "view mate region" —
|
|
10
10
|
* and both had their own copy of the same fifteen lines, including the same two
|
|
11
11
|
* decisions worth stating once:
|
|
12
12
|
*
|
|
@@ -6,7 +6,7 @@ import { transaction } from 'mobx';
|
|
|
6
6
|
*
|
|
7
7
|
* This is the "navigate the view you are looking at" half of a launcher, as
|
|
8
8
|
* against `session.addView`, which opens a second one. Two features do it —
|
|
9
|
-
* plugin-
|
|
9
|
+
* plugin-features's collapsed introns and plugin-alignments' "view mate region" —
|
|
10
10
|
* and both had their own copy of the same fifteen lines, including the same two
|
|
11
11
|
* decisions worth stating once:
|
|
12
12
|
*
|
|
@@ -39,7 +39,9 @@ export async function renderToSvg(model, opts) {
|
|
|
39
39
|
textHeight,
|
|
40
40
|
trackLabels,
|
|
41
41
|
});
|
|
42
|
-
|
|
42
|
+
if (opts.announceSkipped !== false) {
|
|
43
|
+
notifySkippedSvgTracks(session, skippedTracks);
|
|
44
|
+
}
|
|
43
45
|
// read after the displays' waits, and handed to SVGHeader rather than read
|
|
44
46
|
// again, so the reserved `tracksTop` and the drawn header are one layout
|
|
45
47
|
const { width, effectiveShowCytobands } = model;
|
|
@@ -25,7 +25,7 @@ interface CenterKeepingView {
|
|
|
25
25
|
* A caller building a view SNAPSHOT does: it has no model to call
|
|
26
26
|
* `fitAllRegions()` on, and naming `bpPerPx`/`offsetPx` instead is dropped
|
|
27
27
|
* whenever the snapshot also carries a `windowWidthBp` — which one copied off an
|
|
28
|
-
* existing view always does. plugin-
|
|
28
|
+
* existing view always does. plugin-features's collapsed-intron launch is that
|
|
29
29
|
* caller. A live view calls `fitAllRegions()`.
|
|
30
30
|
*
|
|
31
31
|
* `width` enters only through the zoom-in floor, which bites for a region set
|
|
@@ -18,7 +18,7 @@ import { tickLabelsWorthDrawing } from '@jbrowse/core/util/tickLabels';
|
|
|
18
18
|
* A caller building a view SNAPSHOT does: it has no model to call
|
|
19
19
|
* `fitAllRegions()` on, and naming `bpPerPx`/`offsetPx` instead is dropped
|
|
20
20
|
* whenever the snapshot also carries a `windowWidthBp` — which one copied off an
|
|
21
|
-
* existing view always does. plugin-
|
|
21
|
+
* existing view always does. plugin-features's collapsed-intron launch is that
|
|
22
22
|
* caller. A live view calls `fitAllRegions()`.
|
|
23
23
|
*
|
|
24
24
|
* `width` enters only through the zoom-in floor, which bites for a region set
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { FieldPredicate } from '@jbrowse/core/util/fieldPredicate';
|
|
2
|
+
import type { TrackInit } from '@jbrowse/core/util/tracks';
|
|
3
|
+
import type { DisplaySettings } from '@jbrowse/launch-args/displaySettings';
|
|
4
|
+
import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
5
|
+
/** The linear view a command line's tracks open in, as these helpers read it. */
|
|
6
|
+
export interface ModifierView {
|
|
7
|
+
centerLineInfo?: {
|
|
8
|
+
refName?: string;
|
|
9
|
+
offset: number;
|
|
10
|
+
start: number;
|
|
11
|
+
end: number;
|
|
12
|
+
reversed?: boolean;
|
|
13
|
+
oob?: boolean;
|
|
14
|
+
};
|
|
15
|
+
launchTrack: (trackId: string, trackSnapshot?: object, displaySnapshot?: object) => Promise<{
|
|
16
|
+
displays: unknown[];
|
|
17
|
+
} | undefined>;
|
|
18
|
+
showTrack: (trackId: string, trackSnapshot?: object, displaySnapshot?: object) => unknown;
|
|
19
|
+
tracks: {
|
|
20
|
+
configuration: {
|
|
21
|
+
trackId: string;
|
|
22
|
+
};
|
|
23
|
+
displays: unknown[];
|
|
24
|
+
activeDisplay: {
|
|
25
|
+
configuration: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
}[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* `displayTypeOf` for a track the session holds, undefined for one it does
|
|
31
|
+
* not; `displayType` is the display the caller opens it as when no
|
|
32
|
+
* `display:` modifier names one.
|
|
33
|
+
*/
|
|
34
|
+
export declare function openedDisplayType(session: {
|
|
35
|
+
getTrackById: (trackId: string) => unknown;
|
|
36
|
+
}, trackId: string, modifiers: string[], displayType?: string): string | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Write the `filter` of `trackId`'s `settings` onto its display open in
|
|
39
|
+
* `view`, by field over the configured filter, as the track menu would.
|
|
40
|
+
* Returns the display's filter as written, which reopens the track as drawn,
|
|
41
|
+
* or undefined when the settings write no filter.
|
|
42
|
+
*/
|
|
43
|
+
export declare function writeFilter(view: Pick<ModifierView, 'tracks' | 'showTrack'>, trackId: string, settings: DisplaySettings): FieldPredicate[] | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated `writeFilter`, which every display category takes. This one
|
|
46
|
+
* writes an alignments `display`'s filter only.
|
|
47
|
+
*/
|
|
48
|
+
export declare function writeReadFilter(display: unknown, displayType: string | undefined, settings: DisplaySettings): FieldPredicate[] | undefined;
|
|
49
|
+
interface LaunchOptions {
|
|
50
|
+
/** the track entry's own display settings, which the modifiers write over */
|
|
51
|
+
displaySnapshot?: Record<string, unknown>;
|
|
52
|
+
trackSnapshot?: Record<string, unknown>;
|
|
53
|
+
/** changes the settings after the center-line sort resolves, before the open */
|
|
54
|
+
adjust?: (settings: DisplaySettings) => void;
|
|
55
|
+
warn?: (message: string) => void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Open `trackId` in `view` with its command-line modifiers applied, and return
|
|
59
|
+
* the session-spec track entry that reopens it as drawn, or undefined when the
|
|
60
|
+
* open fails, which `launchTrack` reports to the session.
|
|
61
|
+
*/
|
|
62
|
+
export declare function launchTrackWithModifiers(view: ModifierView, trackId: string, displayType: string | undefined, modifiers: string[], { displaySnapshot, trackSnapshot, adjust, warn, }?: LaunchOptions): Promise<{
|
|
63
|
+
trackId: string;
|
|
64
|
+
filter?: FieldPredicate[] | undefined;
|
|
65
|
+
} | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* Open a session spec's track entry in a navigated `view`, its `modifiers`
|
|
68
|
+
* written over its display settings. When a modifier or the open fails, the
|
|
69
|
+
* session's snackbar says why and the track opens with its own settings.
|
|
70
|
+
*/
|
|
71
|
+
export declare function launchTrackInit(view: ModifierView & IAnyStateTreeNode, t: TrackInit): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Write a track's `memberWrites` onto what its display in `view` already has,
|
|
74
|
+
* after every other modifier, so a member write keeps the rest of the setting
|
|
75
|
+
* whatever order the command line gives them in. A display replaces a color
|
|
76
|
+
* or facet object whole, which is why these cannot ride in on the launch
|
|
77
|
+
* snapshot.
|
|
78
|
+
*/
|
|
79
|
+
export declare function writeMembers(view: Pick<ModifierView, 'tracks' | 'showTrack'>, trackId: string, members: string[]): Record<string, unknown>;
|
|
80
|
+
export {};
|