@jbrowse/plugin-data-management 1.5.0 → 1.5.4

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.
Files changed (30) hide show
  1. package/dist/AddTrackWidget/model.d.ts +1 -434
  2. package/dist/AssemblyManager/AssemblyTable.d.ts +8 -2
  3. package/dist/PluginStoreWidget/components/CustomPluginForm.d.ts +1 -1
  4. package/dist/PluginStoreWidget/components/PluginCard.d.ts +2 -2
  5. package/dist/SetDefaultSession/SetDefaultSession.d.ts +4 -6
  6. package/dist/index.d.ts +8 -12
  7. package/dist/plugin-data-management.cjs.development.js +387 -324
  8. package/dist/plugin-data-management.cjs.development.js.map +1 -1
  9. package/dist/plugin-data-management.cjs.production.min.js +1 -1
  10. package/dist/plugin-data-management.cjs.production.min.js.map +1 -1
  11. package/dist/plugin-data-management.esm.js +389 -326
  12. package/dist/plugin-data-management.esm.js.map +1 -1
  13. package/package.json +3 -2
  14. package/src/AddTrackWidget/components/AddTrackWidget.test.js +1 -1
  15. package/src/AddTrackWidget/components/AddTrackWidget.tsx +3 -1
  16. package/src/AddTrackWidget/components/ConfirmTrack.tsx +101 -32
  17. package/src/AddTrackWidget/components/TrackSourceSelect.tsx +2 -3
  18. package/src/AddTrackWidget/index.test.jsx +34 -15
  19. package/src/AddTrackWidget/model.ts +5 -14
  20. package/src/AssemblyManager/AssemblyManager.tsx +3 -1
  21. package/src/AssemblyManager/AssemblyTable.tsx +40 -39
  22. package/src/HierarchicalTrackSelectorWidget/model.js +3 -2
  23. package/src/PluginStoreWidget/components/CustomPluginForm.tsx +164 -56
  24. package/src/PluginStoreWidget/components/InstalledPlugin.tsx +16 -11
  25. package/src/PluginStoreWidget/components/PluginCard.tsx +7 -9
  26. package/src/PluginStoreWidget/components/PluginStoreWidget.test.js +8 -7
  27. package/src/PluginStoreWidget/components/PluginStoreWidget.tsx +34 -25
  28. package/src/PluginStoreWidget/components/__snapshots__/PluginStoreWidget.test.js.snap +69 -52
  29. package/src/SetDefaultSession/SetDefaultSession.test.tsx +6 -81
  30. package/src/SetDefaultSession/SetDefaultSession.tsx +51 -162
@@ -7,36 +7,22 @@ import { ConfigurationSchema, ConfigurationReference, getConf, readConfObject }
7
7
  import { objectHash, getSession, isElectron } from '@jbrowse/core/util';
8
8
  import { types, getParent, getEnv, getRoot } from 'mobx-state-tree';
9
9
  import { openLocation } from '@jbrowse/core/util/io';
10
- import { generateUnknownTrackConf, generateUnsupportedTrackConf, guessAdapter, UNSUPPORTED, guessTrackType, UNKNOWN } from '@jbrowse/core/util/tracks';
10
+ import { generateUnknownTrackConf, generateUnsupportedTrackConf, guessAdapter, getFileName, UNSUPPORTED, guessTrackType, UNKNOWN } from '@jbrowse/core/util/tracks';
11
11
  import { ElementId } from '@jbrowse/core/util/types/mst';
12
12
  import { observer, PropTypes } from 'mobx-react';
13
- import { ListItem, IconButton, Typography, Tooltip, Dialog, DialogTitle, DialogContent, DialogActions, Button, List, Card, CardContent, Link, CardActions, TextField as TextField$1, InputAdornment, Accordion, AccordionSummary, makeStyles as makeStyles$1, Grid, TableRow, TableCell, TableContainer, Paper as Paper$1, Table, TableHead, TableBody, MenuItem, Fab, Menu, FormControlLabel, Checkbox, Stepper, Step, StepLabel, StepContent, DialogContentText } from '@material-ui/core';
13
+ import { ListItem, IconButton, Typography, Tooltip, Dialog, DialogTitle, DialogContent, DialogActions, Button, makeStyles, List, Card, CardContent, Link, CardActions, DialogContentText, TextField, Collapse, InputAdornment, Accordion, AccordionSummary, TableRow, TableCell, TableContainer, Paper, Table, TableHead, TableBody, Grid, MenuItem, Fab, Menu, FormControlLabel, Checkbox, ListSubheader, Stepper, Step, StepLabel, StepContent } from '@material-ui/core';
14
14
  import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
15
15
  import ClearIcon from '@material-ui/icons/Clear';
16
16
  import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
17
- import { makeStyles } from '@material-ui/core/styles';
18
17
  import CloseIcon from '@material-ui/icons/Close';
19
18
  import LockIcon from '@material-ui/icons/Lock';
20
19
  import { isSessionWithSessionPlugins } from '@jbrowse/core/util/types';
21
20
  import PersonIcon from '@material-ui/icons/Person';
22
21
  import AddIcon from '@material-ui/icons/Add';
23
22
  import CheckIcon from '@material-ui/icons/Check';
24
- import Dialog$1 from '@material-ui/core/Dialog';
25
- import DialogTitle$1 from '@material-ui/core/DialogTitle';
26
- import TextField from '@material-ui/core/TextField';
27
- import Button$1 from '@material-ui/core/Button';
23
+ import clsx from 'clsx';
28
24
  import IconButton$1 from '@material-ui/core/IconButton';
29
- import DialogContent$1 from '@material-ui/core/DialogContent';
30
- import DialogActions$1 from '@material-ui/core/DialogActions';
31
- import List$1 from '@material-ui/core/List';
32
- import ListItem$1 from '@material-ui/core/ListItem';
33
- import ListItemIcon from '@material-ui/core/ListItemIcon';
34
- import ListItemText from '@material-ui/core/ListItemText';
35
- import ListSubheader from '@material-ui/core/ListSubheader';
36
- import Paper from '@material-ui/core/Paper';
37
- import Typography$1 from '@material-ui/core/Typography';
38
- import Radio from '@material-ui/core/Radio';
39
- import pluralize from 'pluralize';
25
+ import { makeStyles as makeStyles$1 } from '@material-ui/core/styles';
40
26
  import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';
41
27
  import CreateIcon from '@material-ui/icons/Create';
42
28
  import DeleteIcon from '@material-ui/icons/Delete';
@@ -50,14 +36,16 @@ import PowerOutlinedIcon from '@material-ui/icons/PowerOutlined';
50
36
  import AutoSizer from 'react-virtualized-auto-sizer';
51
37
  import JBrowseMenu from '@jbrowse/core/ui/Menu';
52
38
  import { VariableSizeTree } from 'react-vtree';
53
- import Link$1 from '@material-ui/core/Link';
54
- import MenuItem$1 from '@material-ui/core/MenuItem';
55
- import { FileSelector as FileSelector$1 } from '@jbrowse/core/ui';
56
39
  import { Alert } from '@material-ui/lab';
40
+ import { FileSelector as FileSelector$1 } from '@jbrowse/core/ui';
41
+ import Button$1 from '@material-ui/core/Button';
57
42
  import Step$1 from '@material-ui/core/Step';
58
43
  import StepContent$1 from '@material-ui/core/StepContent';
59
44
  import StepLabel$1 from '@material-ui/core/StepLabel';
60
45
  import Stepper$1 from '@material-ui/core/Stepper';
46
+ import Typography$1 from '@material-ui/core/Typography';
47
+ import MenuItem$1 from '@material-ui/core/MenuItem';
48
+ import TextField$1 from '@material-ui/core/TextField';
61
49
  import OpenInNewIcon from '@material-ui/icons/OpenInNew';
62
50
 
63
51
  function ownKeys(object, enumerableOnly) {
@@ -65,14 +53,9 @@ function ownKeys(object, enumerableOnly) {
65
53
 
66
54
  if (Object.getOwnPropertySymbols) {
67
55
  var symbols = Object.getOwnPropertySymbols(object);
68
-
69
- if (enumerableOnly) {
70
- symbols = symbols.filter(function (sym) {
71
- return Object.getOwnPropertyDescriptor(object, sym).enumerable;
72
- });
73
- }
74
-
75
- keys.push.apply(keys, symbols);
56
+ enumerableOnly && (symbols = symbols.filter(function (sym) {
57
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
58
+ })), keys.push.apply(keys, symbols);
76
59
  }
77
60
 
78
61
  return keys;
@@ -80,19 +63,12 @@ function ownKeys(object, enumerableOnly) {
80
63
 
81
64
  function _objectSpread2(target) {
82
65
  for (var i = 1; i < arguments.length; i++) {
83
- var source = arguments[i] != null ? arguments[i] : {};
84
-
85
- if (i % 2) {
86
- ownKeys(Object(source), true).forEach(function (key) {
87
- _defineProperty(target, key, source[key]);
88
- });
89
- } else if (Object.getOwnPropertyDescriptors) {
90
- Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
91
- } else {
92
- ownKeys(Object(source)).forEach(function (key) {
93
- Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
94
- });
95
- }
66
+ var source = null != arguments[i] ? arguments[i] : {};
67
+ i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
68
+ _defineProperty(target, key, source[key]);
69
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
70
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
71
+ });
96
72
  }
97
73
 
98
74
  return target;
@@ -101,17 +77,11 @@ function _objectSpread2(target) {
101
77
  function _typeof(obj) {
102
78
  "@babel/helpers - typeof";
103
79
 
104
- if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
105
- _typeof = function (obj) {
106
- return typeof obj;
107
- };
108
- } else {
109
- _typeof = function (obj) {
110
- return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
111
- };
112
- }
113
-
114
- return _typeof(obj);
80
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
81
+ return typeof obj;
82
+ } : function (obj) {
83
+ return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
84
+ }, _typeof(obj);
115
85
  }
116
86
 
117
87
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
@@ -169,6 +139,9 @@ function _defineProperties(target, props) {
169
139
  function _createClass(Constructor, protoProps, staticProps) {
170
140
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
171
141
  if (staticProps) _defineProperties(Constructor, staticProps);
142
+ Object.defineProperty(Constructor, "prototype", {
143
+ writable: false
144
+ });
172
145
  return Constructor;
173
146
  }
174
147
 
@@ -192,12 +165,15 @@ function _inherits(subClass, superClass) {
192
165
  throw new TypeError("Super expression must either be null or a function");
193
166
  }
194
167
 
195
- subClass.prototype = Object.create(superClass && superClass.prototype, {
196
- constructor: {
197
- value: subClass,
198
- writable: true,
199
- configurable: true
200
- }
168
+ Object.defineProperty(subClass, "prototype", {
169
+ value: Object.create(superClass && superClass.prototype, {
170
+ constructor: {
171
+ value: subClass,
172
+ writable: true,
173
+ configurable: true
174
+ }
175
+ }),
176
+ writable: false
201
177
  });
202
178
  if (superClass) _setPrototypeOf(subClass, superClass);
203
179
  }
@@ -1719,13 +1695,6 @@ function isAbsoluteUrl() {
1719
1695
  }
1720
1696
  }
1721
1697
 
1722
- function getFileName(track) {
1723
- var uri = 'uri' in track ? track.uri : undefined;
1724
- var localPath = 'localPath' in track ? track.localPath : undefined;
1725
- var blob = 'blobId' in track ? track : undefined;
1726
- return (blob === null || blob === void 0 ? void 0 : blob.name) || (uri === null || uri === void 0 ? void 0 : uri.slice(uri.lastIndexOf('/') + 1)) || (localPath === null || localPath === void 0 ? void 0 : localPath.slice(localPath.lastIndexOf('/') + 1)) || '';
1727
- }
1728
-
1729
1698
  function f(pluginManager) {
1730
1699
  return types.model('AddTrackModel', {
1731
1700
  id: ElementId,
@@ -1781,7 +1750,7 @@ function f(pluginManager) {
1781
1750
  var trackData = self.trackData,
1782
1751
  indexTrackData = self.indexTrackData,
1783
1752
  adapterHint = self.adapterHint;
1784
- return trackData ? guessAdapter(trackData, indexTrackData, getFileName, adapterHint) : undefined;
1753
+ return trackData ? guessAdapter(trackData, indexTrackData, adapterHint, self) : undefined;
1785
1754
  },
1786
1755
 
1787
1756
  get trackName() {
@@ -1793,7 +1762,8 @@ function f(pluginManager) {
1793
1762
 
1794
1763
  var track = self.trackData,
1795
1764
  index = self.indexTrackData;
1796
- return !!(index !== null && index !== void 0 && (_index$uri = index.uri) !== null && _index$uri !== void 0 && _index$uri.startsWith('ftp://') || track !== null && track !== void 0 && (_track$uri = track.uri) !== null && _track$uri !== void 0 && _track$uri.startsWith('ftp://'));
1765
+ return !!( // @ts-ignore
1766
+ index !== null && index !== void 0 && (_index$uri = index.uri) !== null && _index$uri !== void 0 && _index$uri.startsWith('ftp://') || track !== null && track !== void 0 && (_track$uri = track.uri) !== null && _track$uri !== void 0 && _track$uri.startsWith('ftp://'));
1797
1767
  },
1798
1768
 
1799
1769
  get isRelativeTrackUrl() {
@@ -1847,7 +1817,7 @@ function f(pluginManager) {
1847
1817
  },
1848
1818
 
1849
1819
  get trackType() {
1850
- return self.altTrackType || (this.trackAdapter ? guessTrackType(this.trackAdapter.type) : '');
1820
+ return self.altTrackType || (this.trackAdapter ? guessTrackType(this.trackAdapter.type, self) : '');
1851
1821
  }
1852
1822
 
1853
1823
  };
@@ -1889,9 +1859,9 @@ var hasAnyOverlap = function hasAnyOverlap() {
1889
1859
  function passesFilter(filter, config) {
1890
1860
  var name = getTrackName(config);
1891
1861
  var categories = readConfObject(config, 'category') || [];
1892
- var regexp = new RegExp(filter, 'i');
1893
- return !!name.match(regexp) || categories.filter(function (cat) {
1894
- return !!cat.match(regexp);
1862
+ var filterLower = filter.toLowerCase();
1863
+ return !!name.toLowerCase().includes(filterLower) || categories.filter(function (cat) {
1864
+ return !!cat.toLowerCase().includes(filterLower);
1895
1865
  }).length;
1896
1866
  }
1897
1867
 
@@ -2166,15 +2136,17 @@ var useStyles = /*#__PURE__*/makeStyles(function () {
2166
2136
  },
2167
2137
  dialogContainer: {
2168
2138
  margin: 15
2139
+ },
2140
+ lockedPluginTooltip: {
2141
+ marginRight: '0.5rem'
2169
2142
  }
2170
2143
  };
2171
2144
  });
2172
2145
 
2173
2146
  function LockedPlugin() {
2147
+ var classes = useStyles();
2174
2148
  return /*#__PURE__*/React.createElement(Tooltip, {
2175
- style: {
2176
- marginRight: '0.5rem'
2177
- },
2149
+ className: classes.lockedPluginTooltip,
2178
2150
  title: "This plugin was installed by an administrator, you cannot remove it."
2179
2151
  }, /*#__PURE__*/React.createElement(LockIcon, null));
2180
2152
  }
@@ -2194,7 +2166,7 @@ function PluginDialog(_ref) {
2194
2166
  onClick: function onClick() {
2195
2167
  return _onClose();
2196
2168
  }
2197
- }, /*#__PURE__*/React.createElement(CloseIcon, null))), /*#__PURE__*/React.createElement(DialogContent, null, /*#__PURE__*/React.createElement(Typography, null, "Please confirm that you want to remove ", plugin, ":"), /*#__PURE__*/React.createElement(DialogActions, null, /*#__PURE__*/React.createElement(Button, {
2169
+ }, /*#__PURE__*/React.createElement(CloseIcon, null))), /*#__PURE__*/React.createElement(DialogContent, null, /*#__PURE__*/React.createElement(Typography, null, "Please confirm that you want to remove ", plugin, ". Note: if any resources in this session still use this plugin, it may cause your session to crash"), /*#__PURE__*/React.createElement(DialogActions, null, /*#__PURE__*/React.createElement(Button, {
2198
2170
  variant: "contained",
2199
2171
  color: "primary",
2200
2172
  onClick: function onClick() {
@@ -2222,8 +2194,7 @@ function InstalledPlugin(_ref2) {
2222
2194
  dialogPlugin = _useState2[0],
2223
2195
  setDialogPlugin = _useState2[1];
2224
2196
 
2225
- var session = getSession(model); // @ts-ignore
2226
-
2197
+ var session = getSession(model);
2227
2198
  var sessionPlugins = session.sessionPlugins;
2228
2199
  var isSessionPlugin = sessionPlugins === null || sessionPlugins === void 0 ? void 0 : sessionPlugins.some(function (p) {
2229
2200
  return pluginManager.pluginMetadata[plugin.name].url === p.url;
@@ -2236,12 +2207,11 @@ function InstalledPlugin(_ref2) {
2236
2207
  onClose: function onClose(name) {
2237
2208
  if (name) {
2238
2209
  var pluginMetadata = pluginManager.pluginMetadata[plugin.name];
2239
- var pluginUrl = pluginMetadata.url;
2240
2210
 
2241
2211
  if (adminMode) {
2242
- jbrowse.removePlugin(pluginUrl);
2212
+ jbrowse.removePlugin(pluginMetadata);
2243
2213
  } else if (isSessionWithSessionPlugins(session)) {
2244
- session.removeSessionPlugin(pluginUrl);
2214
+ session.removeSessionPlugin(pluginMetadata);
2245
2215
  }
2246
2216
  }
2247
2217
 
@@ -2372,101 +2342,194 @@ function PluginCard(_ref) {
2372
2342
 
2373
2343
  var PluginCard$1 = /*#__PURE__*/observer(PluginCard);
2374
2344
 
2375
- var useStyles$2 = /*#__PURE__*/makeStyles(function () {
2345
+ var useStyles$2 = /*#__PURE__*/makeStyles(function (theme) {
2376
2346
  return {
2377
- closeDialog: {
2347
+ closeButton: {
2378
2348
  position: 'absolute',
2379
- right: 0,
2380
- top: 0
2349
+ right: theme.spacing(1),
2350
+ top: theme.spacing(1)
2381
2351
  },
2382
- dialogContainer: {
2383
- margin: 15,
2352
+ dialogContent: {
2384
2353
  display: 'flex',
2385
2354
  flexDirection: 'column'
2355
+ },
2356
+ expand: {
2357
+ transform: 'rotate(0deg)',
2358
+ marginLeft: 'auto',
2359
+ transition: theme.transitions.create('transform', {
2360
+ duration: theme.transitions.duration.shortest
2361
+ })
2362
+ },
2363
+ expandOpen: {
2364
+ transform: 'rotate(180deg)'
2386
2365
  }
2387
2366
  };
2388
2367
  });
2389
2368
 
2390
2369
  function CustomPluginForm(_ref) {
2391
2370
  var open = _ref.open,
2392
- _onClose = _ref.onClose,
2371
+ onClose = _ref.onClose,
2393
2372
  model = _ref.model;
2394
2373
  var classes = useStyles$2();
2395
2374
 
2396
- var _useState = useState({
2397
- name: '',
2398
- url: ''
2399
- }),
2375
+ var _useState = useState(''),
2400
2376
  _useState2 = _slicedToArray(_useState, 2),
2401
- formInput = _useState2[0],
2402
- setFormInput = _useState2[1];
2377
+ umdPluginName = _useState2[0],
2378
+ setUMDPluginName = _useState2[1];
2403
2379
 
2404
- var handleChange = function handleChange(event) {
2405
- setFormInput(_objectSpread2(_objectSpread2({}, formInput), {}, _defineProperty({}, event.target.name, event.target.value)));
2406
- };
2380
+ var _useState3 = useState(''),
2381
+ _useState4 = _slicedToArray(_useState3, 2),
2382
+ umdPluginUrl = _useState4[0],
2383
+ setUMDPluginUrl = _useState4[1];
2384
+
2385
+ var _useState5 = useState(''),
2386
+ _useState6 = _slicedToArray(_useState5, 2),
2387
+ esmPluginUrl = _useState6[0],
2388
+ setESMPluginUrl = _useState6[1];
2389
+
2390
+ var _useState7 = useState(''),
2391
+ _useState8 = _slicedToArray(_useState7, 2),
2392
+ cjsPluginUrl = _useState8[0],
2393
+ setCJSPluginUrl = _useState8[1];
2394
+
2395
+ var _useState9 = useState(false),
2396
+ _useState10 = _slicedToArray(_useState9, 2),
2397
+ advancedOptionsOpen = _useState10[0],
2398
+ setAdvancedOptionsOpen = _useState10[1];
2399
+
2400
+ function handleChange(event) {
2401
+ var _event$target = event.target,
2402
+ name = _event$target.name,
2403
+ value = _event$target.value;
2404
+
2405
+ if (name === 'umdName') {
2406
+ setUMDPluginName(value);
2407
+ }
2408
+
2409
+ if (name === 'umdUrl') {
2410
+ setUMDPluginUrl(value);
2411
+ }
2412
+
2413
+ if (name === 'esmUrl') {
2414
+ setESMPluginUrl(value);
2415
+ }
2416
+
2417
+ if (name === 'cjsUrl') {
2418
+ setCJSPluginUrl(value);
2419
+ }
2420
+ }
2421
+
2422
+ function handleOpenAdvancedOptions() {
2423
+ setAdvancedOptionsOpen(!advancedOptionsOpen);
2424
+ }
2407
2425
 
2408
2426
  var rootModel = getRoot(model);
2409
2427
  var jbrowse = rootModel.jbrowse;
2428
+ var ready = Boolean(umdPluginName && umdPluginUrl || esmPluginUrl || cjsPluginUrl);
2410
2429
 
2411
- var handleSubmit = function handleSubmit() {
2412
- jbrowse.addPlugin({
2413
- name: formInput.name,
2414
- url: formInput.url
2415
- });
2416
- };
2430
+ function handleSubmit() {
2431
+ if (!ready) {
2432
+ return;
2433
+ }
2417
2434
 
2418
- return /*#__PURE__*/React.createElement(Dialog$1, {
2419
- open: open,
2420
- onClose: function onClose() {
2421
- return _onClose(false);
2435
+ var pluginDefinition = {};
2436
+
2437
+ if (umdPluginName && umdPluginUrl) {
2438
+ pluginDefinition.name = umdPluginName;
2439
+ pluginDefinition.umdUrl = umdPluginUrl;
2422
2440
  }
2423
- }, /*#__PURE__*/React.createElement(DialogTitle$1, null, /*#__PURE__*/React.createElement(IconButton$1, {
2424
- className: classes.closeDialog,
2425
- "aria-label": "close-dialog",
2441
+
2442
+ if (esmPluginUrl) {
2443
+ pluginDefinition.esmUrl = esmPluginUrl;
2444
+ }
2445
+
2446
+ if (cjsPluginUrl) {
2447
+ pluginDefinition.cjsUrl = cjsPluginUrl;
2448
+ }
2449
+
2450
+ jbrowse.addPlugin(pluginDefinition);
2451
+ }
2452
+
2453
+ function handleClose() {
2454
+ setUMDPluginName('');
2455
+ setUMDPluginUrl('');
2456
+ setESMPluginUrl('');
2457
+ setCJSPluginUrl('');
2458
+ onClose();
2459
+ }
2460
+
2461
+ return /*#__PURE__*/React.createElement(Dialog, {
2462
+ open: open,
2463
+ onClose: handleClose
2464
+ }, /*#__PURE__*/React.createElement(DialogTitle, null, "Add custom plugin", /*#__PURE__*/React.createElement(IconButton$1, {
2465
+ size: "medium",
2466
+ className: classes.closeButton,
2426
2467
  onClick: function onClick() {
2427
- return _onClose(false);
2468
+ return onClose();
2428
2469
  }
2429
- }, /*#__PURE__*/React.createElement(CloseIcon, null))), /*#__PURE__*/React.createElement("div", {
2430
- className: classes.dialogContainer
2431
- }, /*#__PURE__*/React.createElement(TextField, {
2432
- id: "name-input",
2433
- name: "name",
2470
+ }, /*#__PURE__*/React.createElement(CloseIcon, null))), /*#__PURE__*/React.createElement("form", {
2471
+ onSubmit: handleSubmit
2472
+ }, /*#__PURE__*/React.createElement(DialogContent, {
2473
+ className: classes.dialogContent
2474
+ }, /*#__PURE__*/React.createElement(DialogContentText, null, "Enter the name of the plugin and its URL. The name should match what is defined in the plugin's build."), /*#__PURE__*/React.createElement(TextField, {
2475
+ id: "umd-name-input",
2476
+ name: "umdName",
2434
2477
  label: "Plugin name",
2435
2478
  variant: "outlined",
2436
- value: formInput.name,
2437
- onChange: handleChange,
2438
- multiline: true
2479
+ value: umdPluginName,
2480
+ onChange: handleChange
2439
2481
  }), /*#__PURE__*/React.createElement(TextField, {
2440
- id: "url-input",
2441
- name: "url",
2482
+ id: "umd-url-input",
2483
+ name: "umdUrl",
2442
2484
  label: "Plugin URL",
2443
2485
  variant: "outlined",
2444
- value: formInput.url,
2445
- onChange: handleChange,
2446
- multiline: true
2447
- }), /*#__PURE__*/React.createElement(Button$1, {
2486
+ value: umdPluginUrl,
2487
+ onChange: handleChange
2488
+ }), /*#__PURE__*/React.createElement(DialogContentText, {
2489
+ onClick: handleOpenAdvancedOptions
2490
+ }, /*#__PURE__*/React.createElement(IconButton$1, {
2491
+ className: clsx(classes.expand, _defineProperty({}, classes.expandOpen, advancedOptionsOpen)),
2492
+ "aria-expanded": advancedOptionsOpen,
2493
+ "aria-label": "show more"
2494
+ }, /*#__PURE__*/React.createElement(ExpandMoreIcon, null)), "Advanced options"), /*#__PURE__*/React.createElement(Collapse, {
2495
+ "in": advancedOptionsOpen
2496
+ }, /*#__PURE__*/React.createElement("div", {
2497
+ className: classes.dialogContent
2498
+ }, /*#__PURE__*/React.createElement(DialogContentText, null, "The above fields assume that the plugin is built in UMD format. If your plugin is in another format, or you have additional builds you want to add (such as a CJS build for using NodeJS APIs in desktop), you can enter the URLs for those builds below."), /*#__PURE__*/React.createElement(TextField, {
2499
+ id: "esm-url-input",
2500
+ name: "esmUrl",
2501
+ label: "ESM build URL",
2502
+ variant: "outlined",
2503
+ value: esmPluginUrl,
2504
+ onChange: handleChange
2505
+ }), /*#__PURE__*/React.createElement(TextField, {
2506
+ id: "cjs-url-input",
2507
+ name: "cjsUrl",
2508
+ label: "CJS build URL",
2509
+ variant: "outlined",
2510
+ value: cjsPluginUrl,
2511
+ onChange: handleChange
2512
+ })))), /*#__PURE__*/React.createElement(DialogActions, null, /*#__PURE__*/React.createElement(Button, {
2513
+ variant: "contained",
2514
+ onClick: handleClose
2515
+ }, "Cancel"), /*#__PURE__*/React.createElement(Button, {
2448
2516
  variant: "contained",
2449
2517
  color: "primary",
2450
- style: {
2451
- marginTop: '1.5rem'
2452
- },
2453
- onClick: handleSubmit
2454
- }, "Add plugin")));
2518
+ onClick: handleSubmit,
2519
+ disabled: !ready
2520
+ }, "Submit"))));
2455
2521
  }
2456
2522
 
2457
2523
  var CustomPluginForm$1 = /*#__PURE__*/observer(CustomPluginForm);
2458
2524
 
2459
- var useStyles$3 = /*#__PURE__*/makeStyles$1(function (theme) {
2525
+ var useStyles$3 = /*#__PURE__*/makeStyles(function (theme) {
2460
2526
  return {
2461
- accordion: {
2462
- marginTop: '1em'
2527
+ root: {
2528
+ margin: theme.spacing(1)
2463
2529
  },
2464
2530
  expandIcon: {
2465
2531
  color: '#fff'
2466
2532
  },
2467
- searchBox: {
2468
- marginBottom: theme.spacing(2)
2469
- },
2470
2533
  adminBadge: {
2471
2534
  margin: '0.5em',
2472
2535
  borderRadius: 3,
@@ -2488,7 +2551,7 @@ function PluginStoreWidget(_ref) {
2488
2551
  var model = _ref.model;
2489
2552
  var classes = useStyles$3();
2490
2553
 
2491
- var _useState = useState([]),
2554
+ var _useState = useState(),
2492
2555
  _useState2 = _slicedToArray(_useState, 2),
2493
2556
  pluginArray = _useState2[0],
2494
2557
  setPluginArray = _useState2[1];
@@ -2510,61 +2573,71 @@ function PluginStoreWidget(_ref) {
2510
2573
  pluginManager = _getEnv.pluginManager;
2511
2574
 
2512
2575
  useEffect(function () {
2513
- var killed = false;
2576
+ var controller = new AbortController();
2577
+ var signal = controller.signal;
2514
2578
 
2515
2579
  _asyncToGenerator( /*#__PURE__*/runtime_1.mark(function _callee() {
2516
- var fetchResult, array;
2580
+ var response, err, array;
2517
2581
  return runtime_1.wrap(function _callee$(_context) {
2518
2582
  while (1) {
2519
2583
  switch (_context.prev = _context.next) {
2520
2584
  case 0:
2521
2585
  _context.prev = 0;
2522
2586
  _context.next = 3;
2523
- return fetch('https://jbrowse.org/plugin-store/plugins.json');
2587
+ return fetch('https://jbrowse.org/plugin-store/plugins.json', {
2588
+ signal: signal
2589
+ });
2524
2590
 
2525
2591
  case 3:
2526
- fetchResult = _context.sent;
2592
+ response = _context.sent;
2527
2593
 
2528
- if (fetchResult.ok) {
2529
- _context.next = 6;
2594
+ if (response.ok) {
2595
+ _context.next = 9;
2530
2596
  break;
2531
2597
  }
2532
2598
 
2533
- throw new Error('Failed to fetch plugin data');
2599
+ _context.next = 7;
2600
+ return response.text();
2601
+
2602
+ case 7:
2603
+ err = _context.sent;
2604
+ throw new Error("Failed to fetch plugin data: ".concat(response.status, " ").concat(response.statusText, " ").concat(err));
2534
2605
 
2535
- case 6:
2536
- _context.next = 8;
2537
- return fetchResult.json();
2606
+ case 9:
2607
+ _context.next = 11;
2608
+ return response.json();
2538
2609
 
2539
- case 8:
2610
+ case 11:
2540
2611
  array = _context.sent;
2541
2612
 
2542
- if (!killed) {
2613
+ if (!signal.aborted) {
2543
2614
  setPluginArray(array.plugins);
2544
2615
  }
2545
2616
 
2546
- _context.next = 16;
2617
+ _context.next = 19;
2547
2618
  break;
2548
2619
 
2549
- case 12:
2550
- _context.prev = 12;
2620
+ case 15:
2621
+ _context.prev = 15;
2551
2622
  _context.t0 = _context["catch"](0);
2552
2623
  console.error(_context.t0);
2553
2624
  setError(_context.t0);
2554
2625
 
2555
- case 16:
2626
+ case 19:
2556
2627
  case "end":
2557
2628
  return _context.stop();
2558
2629
  }
2559
2630
  }
2560
- }, _callee, null, [[0, 12]]);
2631
+ }, _callee, null, [[0, 15]]);
2561
2632
  }))();
2562
2633
 
2563
2634
  return function () {
2564
- killed = true;
2635
+ controller.abort();
2565
2636
  };
2566
2637
  }, []);
2567
- return /*#__PURE__*/React.createElement("div", null, adminMode && /*#__PURE__*/React.createElement(React.Fragment, null, !isElectron && /*#__PURE__*/React.createElement("div", {
2638
+ return /*#__PURE__*/React.createElement("div", {
2639
+ className: classes.root
2640
+ }, adminMode && /*#__PURE__*/React.createElement(React.Fragment, null, !isElectron && /*#__PURE__*/React.createElement("div", {
2568
2641
  className: classes.adminBadge
2569
2642
  }, /*#__PURE__*/React.createElement(InfoOutlinedIcon, {
2570
2643
  style: {
@@ -2580,10 +2653,11 @@ function PluginStoreWidget(_ref) {
2580
2653
  }
2581
2654
  }, "Add custom plugin")), /*#__PURE__*/React.createElement(CustomPluginForm$1, {
2582
2655
  open: customPluginFormOpen,
2583
- onClose: setCustomPluginFormOpen,
2656
+ onClose: function onClose() {
2657
+ return setCustomPluginFormOpen(false);
2658
+ },
2584
2659
  model: model
2585
- })), /*#__PURE__*/React.createElement(TextField$1, {
2586
- className: classes.searchBox,
2660
+ })), /*#__PURE__*/React.createElement(TextField, {
2587
2661
  label: "Filter plugins",
2588
2662
  value: model.filterText,
2589
2663
  onChange: function onChange(event) {
@@ -2625,7 +2699,12 @@ function PluginStoreWidget(_ref) {
2625
2699
  variant: "h5"
2626
2700
  }, "Available plugins")), error ? /*#__PURE__*/React.createElement(Typography, {
2627
2701
  color: "error"
2628
- }, "".concat(error)) : pluginArray.length ? pluginArray.filter(function (plugin) {
2702
+ }, "".concat(error)) : pluginArray ? pluginArray.filter(function (plugin) {
2703
+ // If pugin only has cjsUrl, don't display outside desktop
2704
+ if (!isElectron && !(plugin.esmUrl || plugin.url || plugin.umdUrl)) {
2705
+ return false;
2706
+ }
2707
+
2629
2708
  return plugin.name.toLowerCase().includes(model.filterText.toLowerCase());
2630
2709
  }).map(function (plugin) {
2631
2710
  return /*#__PURE__*/React.createElement(PluginCard$1, {
@@ -2756,128 +2835,50 @@ var _default = /*#__PURE__*/function (_Plugin) {
2756
2835
  return _default;
2757
2836
  }(Plugin);
2758
2837
 
2759
- var useStyles$4 = /*#__PURE__*/makeStyles(function (theme) {
2760
- return {
2761
- root: {
2762
- margin: theme.spacing(1)
2763
- },
2764
- message: {
2765
- padding: theme.spacing(3)
2766
- },
2767
- titleBox: {
2768
- color: '#fff',
2769
- backgroundColor: theme.palette.primary.main,
2770
- textAlign: 'center'
2771
- },
2772
- dialogContent: {
2773
- width: 600
2774
- },
2775
- resetButton: {
2776
- justifyContent: 'center',
2777
- marginBottom: '6px'
2778
- }
2779
- };
2780
- });
2781
- var CurrentSession = /*#__PURE__*/observer(function (_ref) {
2782
- var session = _ref.session,
2783
- selectedDefault = _ref.selectedDefault,
2784
- handleRadio = _ref.handleRadio;
2785
- var classes = useStyles$4();
2786
- return /*#__PURE__*/React.createElement(Paper, {
2787
- className: classes.root
2788
- }, /*#__PURE__*/React.createElement(List$1, {
2789
- subheader: /*#__PURE__*/React.createElement(ListSubheader, null, "Currently open session")
2790
- }, /*#__PURE__*/React.createElement(ListItem$1, null, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(Radio, {
2791
- checked: session.name === selectedDefault,
2792
- onChange: function onChange() {
2793
- return handleRadio(session);
2794
- }
2795
- })), /*#__PURE__*/React.createElement(ListItemText, {
2796
- primary: session.name
2797
- }))));
2798
- });
2799
- var SetDefaultSession$1 = /*#__PURE__*/observer(function (_ref2) {
2800
- var rootModel = _ref2.rootModel,
2801
- open = _ref2.open,
2802
- onClose = _ref2.onClose,
2803
- currentDefault = _ref2.currentDefault;
2804
- var classes = useStyles$4();
2805
- var session = rootModel.session;
2838
+ function canSetDefaultSession(obj) {
2839
+ return _typeof(obj) === 'object' && !!obj && 'jbrowse' in obj;
2840
+ }
2806
2841
 
2807
- var _useState = useState(currentDefault),
2808
- _useState2 = _slicedToArray(_useState, 2),
2809
- selectedDefault = _useState2[0],
2810
- setSelectedDefault = _useState2[1]; // eslint-disable-next-line @typescript-eslint/no-explicit-any
2842
+ var SetDefaultSession$1 = /*#__PURE__*/observer(function (_ref) {
2843
+ var rootModel = _ref.rootModel,
2844
+ onClose = _ref.onClose;
2811
2845
 
2846
+ if (!rootModel) {
2847
+ return null;
2848
+ }
2812
2849
 
2813
- function handleRadio(sessionSnapshot) {
2814
- setSelectedDefault(sessionSnapshot.name);
2815
- rootModel.jbrowse.setDefaultSessionConf(sessionSnapshot);
2816
- session.notify("Set default session to ".concat(sessionSnapshot.name), 'success');
2850
+ if (!canSetDefaultSession(rootModel)) {
2851
+ console.error('Incorrect rootmodel');
2852
+ return null;
2817
2853
  }
2818
2854
 
2819
- return /*#__PURE__*/React.createElement(Dialog$1, {
2820
- open: open
2821
- }, /*#__PURE__*/React.createElement(DialogTitle$1, {
2822
- className: classes.titleBox
2823
- }, "Set Default Session"), /*#__PURE__*/React.createElement(DialogContent$1, null, /*#__PURE__*/React.createElement(Grid, {
2824
- className: classes.resetButton,
2825
- container: true
2826
- }, /*#__PURE__*/React.createElement(Grid, {
2827
- item: true
2828
- }, /*#__PURE__*/React.createElement(Button$1, {
2829
- color: "secondary",
2855
+ var jbrowse = rootModel.jbrowse,
2856
+ session = rootModel.session;
2857
+ return /*#__PURE__*/React.createElement(Dialog, {
2858
+ open: true,
2859
+ onClose: onClose
2860
+ }, /*#__PURE__*/React.createElement(DialogTitle, null, "Set default session"), /*#__PURE__*/React.createElement(DialogContent, null, /*#__PURE__*/React.createElement(Typography, null, "Select \"Set current session as default\" to make your current session saved to the config file. You can also hit \"Clear default session\", which would remove the default session from the config.")), /*#__PURE__*/React.createElement(DialogActions, null, /*#__PURE__*/React.createElement(Button, {
2830
2861
  variant: "contained",
2831
2862
  onClick: function onClick() {
2832
- setSelectedDefault('New session');
2833
- rootModel.jbrowse.setDefaultSessionConf({
2863
+ jbrowse.setDefaultSessionConf({
2834
2864
  name: "New session"
2835
2865
  });
2836
- session.notify('Reset default session', 'success');
2866
+ onClose();
2837
2867
  }
2838
- }, "Clear default session"))), /*#__PURE__*/React.createElement(CurrentSession, {
2839
- session: session,
2840
- selectedDefault: selectedDefault,
2841
- handleRadio: handleRadio
2842
- }), /*#__PURE__*/React.createElement(Paper, {
2843
- className: classes.root
2844
- }, /*#__PURE__*/React.createElement(List$1, {
2845
- subheader: /*#__PURE__*/React.createElement(ListSubheader, null, "Saved sessions")
2846
- }, session.savedSessions.length ? session.savedSessions.map( // eslint-disable-next-line @typescript-eslint/no-explicit-any
2847
- function (sessionSnapshot) {
2848
- var _sessionSnapshot$view = sessionSnapshot.views,
2849
- views = _sessionSnapshot$view === void 0 ? [] : _sessionSnapshot$view;
2850
- var totalTracks = views // eslint-disable-next-line @typescript-eslint/no-explicit-any
2851
- .map(function (view) {
2852
- return view.tracks.length;
2853
- }).reduce(function (a, b) {
2854
- return a + b;
2855
- }, 0);
2856
-
2857
- if (sessionSnapshot.name !== session.name) {
2858
- return /*#__PURE__*/React.createElement(ListItem$1, {
2859
- key: sessionSnapshot.name
2860
- }, /*#__PURE__*/React.createElement(ListItemIcon, null, /*#__PURE__*/React.createElement(Radio, {
2861
- checked: sessionSnapshot.name === selectedDefault,
2862
- onChange: function onChange() {
2863
- return handleRadio(sessionSnapshot);
2864
- }
2865
- })), /*#__PURE__*/React.createElement(ListItemText, {
2866
- primary: sessionSnapshot.name,
2867
- secondary: "".concat(views.length, " ").concat(pluralize('view', views.length), "; ").concat(totalTracks, "\n open ").concat(pluralize('track', totalTracks))
2868
- }));
2869
- }
2870
-
2871
- return null;
2872
- }) : /*#__PURE__*/React.createElement(Typography$1, {
2873
- className: classes.message
2874
- }, "No saved sessions found")))), /*#__PURE__*/React.createElement(DialogActions$1, null, /*#__PURE__*/React.createElement(Button$1, {
2868
+ }, "Clear default session"), /*#__PURE__*/React.createElement(Button, {
2875
2869
  color: "secondary",
2876
2870
  variant: "contained",
2877
2871
  onClick: function onClick() {
2878
- onClose(false);
2872
+ return onClose();
2873
+ }
2874
+ }, "Cancel"), /*#__PURE__*/React.createElement(Button, {
2875
+ color: "primary",
2876
+ variant: "contained",
2877
+ onClick: function onClick() {
2878
+ jbrowse.setDefaultSessionConf(session);
2879
+ onClose();
2879
2880
  }
2880
- }, "Return")));
2881
+ }, "Set current session as default")));
2881
2882
  });
2882
2883
 
2883
2884
 
@@ -2887,7 +2888,7 @@ var index = {
2887
2888
  'default': SetDefaultSession$1
2888
2889
  };
2889
2890
 
2890
- var useStyles$5 = /*#__PURE__*/makeStyles$1(function () {
2891
+ var useStyles$4 = /*#__PURE__*/makeStyles(function () {
2891
2892
  return {
2892
2893
  table: {
2893
2894
  minWidth: 500,
@@ -2908,7 +2909,7 @@ var AssemblyTable = /*#__PURE__*/observer(function (_ref) {
2908
2909
  var rootModel = _ref.rootModel,
2909
2910
  setIsAssemblyBeingEdited = _ref.setIsAssemblyBeingEdited,
2910
2911
  setAssemblyBeingEdited = _ref.setAssemblyBeingEdited;
2911
- var classes = useStyles$5();
2912
+ var classes = useStyles$4();
2912
2913
 
2913
2914
  function removeAssembly(name) {
2914
2915
  rootModel.jbrowse.removeAssemblyConf(name);
@@ -2942,7 +2943,7 @@ var AssemblyTable = /*#__PURE__*/observer(function (_ref) {
2942
2943
  }))));
2943
2944
  });
2944
2945
  return /*#__PURE__*/React.createElement(TableContainer, {
2945
- component: Paper$1
2946
+ component: Paper
2946
2947
  }, /*#__PURE__*/React.createElement(Table, {
2947
2948
  className: classes.table
2948
2949
  }, /*#__PURE__*/React.createElement(TableHead, null, /*#__PURE__*/React.createElement(TableRow, null, /*#__PURE__*/React.createElement(TableCell, null, /*#__PURE__*/React.createElement(Typography, {
@@ -2956,7 +2957,7 @@ var AssemblyTable = /*#__PURE__*/observer(function (_ref) {
2956
2957
  }, "Actions")))), /*#__PURE__*/React.createElement(TableBody, null, rows)));
2957
2958
  });
2958
2959
 
2959
- var useStyles$6 = /*#__PURE__*/makeStyles$1(function (theme) {
2960
+ var useStyles$5 = /*#__PURE__*/makeStyles(function (theme) {
2960
2961
  return {
2961
2962
  root: {
2962
2963
  flexGrow: 1,
@@ -2983,7 +2984,7 @@ var AdapterSelector = /*#__PURE__*/observer(function (_ref) {
2983
2984
  var adapterSelection = _ref.adapterSelection,
2984
2985
  setAdapterSelection = _ref.setAdapterSelection,
2985
2986
  adapterTypes = _ref.adapterTypes;
2986
- return /*#__PURE__*/React.createElement(TextField$1, {
2987
+ return /*#__PURE__*/React.createElement(TextField, {
2987
2988
  value: adapterSelection,
2988
2989
  label: "Type",
2989
2990
  select: true,
@@ -3074,7 +3075,7 @@ var blank = {
3074
3075
  var AssemblyAddForm = /*#__PURE__*/observer(function (_ref3) {
3075
3076
  var rootModel = _ref3.rootModel,
3076
3077
  setFormOpen = _ref3.setFormOpen;
3077
- var classes = useStyles$6();
3078
+ var classes = useStyles$5();
3078
3079
  var adapterTypes = ['IndexedFastaAdapter', 'BgzipFastaAdapter', 'TwoBitAdapter'];
3079
3080
 
3080
3081
  var _useState = useState(''),
@@ -3170,9 +3171,9 @@ var AssemblyAddForm = /*#__PURE__*/observer(function (_ref3) {
3170
3171
 
3171
3172
  return /*#__PURE__*/React.createElement("div", {
3172
3173
  className: classes.root
3173
- }, /*#__PURE__*/React.createElement(Paper$1, {
3174
+ }, /*#__PURE__*/React.createElement(Paper, {
3174
3175
  className: classes.paper
3175
- }, /*#__PURE__*/React.createElement(TextField$1, {
3176
+ }, /*#__PURE__*/React.createElement(TextField, {
3176
3177
  id: "assembly-name",
3177
3178
  inputProps: {
3178
3179
  'data-testid': 'assembly-name'
@@ -3184,7 +3185,7 @@ var AssemblyAddForm = /*#__PURE__*/observer(function (_ref3) {
3184
3185
  onChange: function onChange(event) {
3185
3186
  return setAssemblyName(event.target.value);
3186
3187
  }
3187
- }), /*#__PURE__*/React.createElement(TextField$1, {
3188
+ }), /*#__PURE__*/React.createElement(TextField, {
3188
3189
  id: "assembly-name",
3189
3190
  inputProps: {
3190
3191
  'data-testid': 'assembly-display-name'
@@ -3236,7 +3237,7 @@ var AssemblyEditor = /*#__PURE__*/observer(function (_ref) {
3236
3237
  });
3237
3238
  });
3238
3239
 
3239
- var useStyles$7 = /*#__PURE__*/makeStyles(function (theme) {
3240
+ var useStyles$6 = /*#__PURE__*/makeStyles$1(function (theme) {
3240
3241
  return {
3241
3242
  titleBox: {
3242
3243
  color: '#fff',
@@ -3263,7 +3264,7 @@ var useStyles$7 = /*#__PURE__*/makeStyles(function (theme) {
3263
3264
  var AssemblyManager$1 = /*#__PURE__*/observer(function (_ref) {
3264
3265
  var rootModel = _ref.rootModel,
3265
3266
  _onClose = _ref.onClose;
3266
- var classes = useStyles$7();
3267
+ var classes = useStyles$6();
3267
3268
 
3268
3269
  var _useState = useState(false),
3269
3270
  _useState2 = _slicedToArray(_useState, 2),
@@ -3360,7 +3361,7 @@ var ManageConnectionsDialog = /*#__PURE__*/lazy(function () {
3360
3361
  var ToggleConnectionsDialog = /*#__PURE__*/lazy(function () {
3361
3362
  return Promise.resolve().then(function () { return ToggleConnectionsDialog$2; });
3362
3363
  });
3363
- var useStyles$8 = /*#__PURE__*/makeStyles$1(function (theme) {
3364
+ var useStyles$7 = /*#__PURE__*/makeStyles(function (theme) {
3364
3365
  var _theme$palette$tertia, _theme$palette$tertia2;
3365
3366
 
3366
3367
  return {
@@ -3432,7 +3433,7 @@ var Node = function Node(props) {
3432
3433
  conf = data.conf,
3433
3434
  onMoreInfo = data.onMoreInfo,
3434
3435
  drawerPosition = data.drawerPosition;
3435
- var classes = useStyles$8();
3436
+ var classes = useStyles$7();
3436
3437
  var width = 10;
3437
3438
  var marginLeft = nestingLevel * width + (isLeaf ? width : 0);
3438
3439
  var unsupported = name && (name.endsWith('(Unsupported)') || name.endsWith('(Unknown)'));
@@ -3658,7 +3659,7 @@ var HierarchicalTrackSelectorContainer = /*#__PURE__*/observer(function (_ref5)
3658
3659
  var model = _ref5.model,
3659
3660
  toolbarHeight = _ref5.toolbarHeight,
3660
3661
  overrideDimensions = _ref5.overrideDimensions;
3661
- var classes = useStyles$8();
3662
+ var classes = useStyles$7();
3662
3663
  var session = getSession(model);
3663
3664
 
3664
3665
  var _useState3 = useState(null),
@@ -3709,7 +3710,7 @@ var HierarchicalTrackSelectorHeader = /*#__PURE__*/observer(function (_ref6) {
3709
3710
  setHeaderHeight = _ref6.setHeaderHeight,
3710
3711
  setAssemblyIdx = _ref6.setAssemblyIdx,
3711
3712
  assemblyIdx = _ref6.assemblyIdx;
3712
- var classes = useStyles$8();
3713
+ var classes = useStyles$7();
3713
3714
  var session = getSession(model);
3714
3715
 
3715
3716
  var _useState5 = useState(),
@@ -3823,7 +3824,7 @@ var HierarchicalTrackSelectorHeader = /*#__PURE__*/observer(function (_ref6) {
3823
3824
  onClick: function onClick(event) {
3824
3825
  setConnectionAnchorEl(event.currentTarget);
3825
3826
  }
3826
- }, /*#__PURE__*/React.createElement(PowerOutlinedIcon, null)), /*#__PURE__*/React.createElement(TextField$1, {
3827
+ }, /*#__PURE__*/React.createElement(PowerOutlinedIcon, null)), /*#__PURE__*/React.createElement(TextField, {
3827
3828
  className: classes.searchBox,
3828
3829
  label: "Filter tracks",
3829
3830
  value: model.filterText,
@@ -3927,7 +3928,7 @@ var HierarchicalTrackSelector$1 = {
3927
3928
  'default': HierarchicalTrackSelectorContainer
3928
3929
  };
3929
3930
 
3930
- var useStyles$9 = /*#__PURE__*/makeStyles(function (theme) {
3931
+ var useStyles$8 = /*#__PURE__*/makeStyles(function (theme) {
3931
3932
  return {
3932
3933
  spacing: {
3933
3934
  marginBottom: theme.spacing(3)
@@ -3940,24 +3941,72 @@ function StatusMessage(_ref) {
3940
3941
 
3941
3942
  var trackAdapter = _ref.trackAdapter,
3942
3943
  trackType = _ref.trackType;
3943
- var classes = useStyles$9();
3944
- return trackAdapter.type === 'SNPCoverageAdapter' ? /*#__PURE__*/React.createElement(Typography$1, {
3944
+ var classes = useStyles$8();
3945
+ return trackAdapter.type === 'SNPCoverageAdapter' ? /*#__PURE__*/React.createElement(Typography, {
3945
3946
  className: classes.spacing
3946
- }, "Selected ", /*#__PURE__*/React.createElement("code", null, trackType), ". Using adapter", ' ', /*#__PURE__*/React.createElement("code", null, trackAdapter.type), " with subadapter", ' ', /*#__PURE__*/React.createElement("code", null, (_trackAdapter$subadap = trackAdapter.subadapter) === null || _trackAdapter$subadap === void 0 ? void 0 : _trackAdapter$subadap.type), ". Please enter a track name and, if necessary, update the track type.") : /*#__PURE__*/React.createElement(Typography$1, {
3947
+ }, "Selected ", /*#__PURE__*/React.createElement("code", null, trackType), ". Using adapter", ' ', /*#__PURE__*/React.createElement("code", null, trackAdapter.type), " with subadapter", ' ', /*#__PURE__*/React.createElement("code", null, (_trackAdapter$subadap = trackAdapter.subadapter) === null || _trackAdapter$subadap === void 0 ? void 0 : _trackAdapter$subadap.type), ". Please enter a track name and, if necessary, update the track type.") : /*#__PURE__*/React.createElement(Typography, {
3947
3948
  className: classes.spacing
3948
3949
  }, "Using adapter ", /*#__PURE__*/React.createElement("code", null, trackAdapter.type), " and guessing track type", ' ', /*#__PURE__*/React.createElement("code", null, trackType), ". Please enter a track name and, if necessary, update the track type.");
3949
3950
  }
3951
+ /**
3952
+ * categorizeAdapters takes a list of adapters and sorts their menu item elements under an appropriate ListSubheader
3953
+ * element. In this way, adapters that are from external plugins can have headers that differentiate them from the
3954
+ * out-of-the-box plugins.
3955
+ * @param adaptersList - a list of adapters found in the PluginManager
3956
+ * @returns a series of JSX elements that are ListSubheaders followed by the adapters
3957
+ * found under that subheader
3958
+ */
3959
+
3960
+
3961
+ function categorizeAdapters(adaptersList) {
3962
+ var currentCategory = ''; // eslint-disable-next-line @typescript-eslint/no-explicit-any
3963
+
3964
+ var items = [];
3965
+ adaptersList.forEach(function (adapter) {
3966
+ var _adapter$adapterMetad;
3967
+
3968
+ if ((_adapter$adapterMetad = adapter.adapterMetadata) !== null && _adapter$adapterMetad !== void 0 && _adapter$adapterMetad.category) {
3969
+ var _adapter$adapterMetad2, _adapter$adapterMetad7, _adapter$adapterMetad8;
3970
+
3971
+ if (currentCategory !== ((_adapter$adapterMetad2 = adapter.adapterMetadata) === null || _adapter$adapterMetad2 === void 0 ? void 0 : _adapter$adapterMetad2.category)) {
3972
+ var _adapter$adapterMetad3, _adapter$adapterMetad4, _adapter$adapterMetad5, _adapter$adapterMetad6;
3973
+
3974
+ currentCategory = (_adapter$adapterMetad3 = adapter.adapterMetadata) === null || _adapter$adapterMetad3 === void 0 ? void 0 : _adapter$adapterMetad3.category;
3975
+ items.push( /*#__PURE__*/React.createElement(ListSubheader, {
3976
+ key: (_adapter$adapterMetad4 = adapter.adapterMetadata) === null || _adapter$adapterMetad4 === void 0 ? void 0 : _adapter$adapterMetad4.category,
3977
+ value: (_adapter$adapterMetad5 = adapter.adapterMetadata) === null || _adapter$adapterMetad5 === void 0 ? void 0 : _adapter$adapterMetad5.category
3978
+ }, (_adapter$adapterMetad6 = adapter.adapterMetadata) === null || _adapter$adapterMetad6 === void 0 ? void 0 : _adapter$adapterMetad6.category));
3979
+ }
3980
+
3981
+ items.push( /*#__PURE__*/React.createElement(MenuItem, {
3982
+ key: adapter.name,
3983
+ value: adapter.name
3984
+ }, (_adapter$adapterMetad7 = adapter.adapterMetadata) !== null && _adapter$adapterMetad7 !== void 0 && _adapter$adapterMetad7.displayName ? (_adapter$adapterMetad8 = adapter.adapterMetadata) === null || _adapter$adapterMetad8 === void 0 ? void 0 : _adapter$adapterMetad8.displayName : adapter.name));
3985
+ }
3986
+ });
3987
+ return items;
3988
+ }
3989
+
3990
+ function getAdapterTypes(pluginManager) {
3991
+ return pluginManager.getElementTypesInGroup('adapter');
3992
+ }
3993
+
3994
+ function getTrackTypes(pluginManager) {
3995
+ return pluginManager.getElementTypesInGroup('track');
3996
+ }
3950
3997
 
3951
3998
  var TrackAdapterSelector = /*#__PURE__*/observer(function (_ref2) {
3952
3999
  var model = _ref2.model;
3953
- var classes = useStyles$9();
4000
+ var classes = useStyles$8();
3954
4001
  var session = getSession(model);
3955
- var trackAdapter = model.trackAdapter;
4002
+ var trackAdapter = model.trackAdapter; // prettier-ignore
4003
+
4004
+ var adapters = getAdapterTypes(getEnv(session).pluginManager);
3956
4005
  return /*#__PURE__*/React.createElement(TextField, {
3957
4006
  className: classes.spacing,
3958
- value: trackAdapter === null || trackAdapter === void 0 ? void 0 : trackAdapter.type,
4007
+ value: (trackAdapter === null || trackAdapter === void 0 ? void 0 : trackAdapter.type) !== 'UNKNOWN' ? trackAdapter === null || trackAdapter === void 0 ? void 0 : trackAdapter.type : '',
3959
4008
  label: "adapterType",
3960
- helperText: "An adapter type",
4009
+ helperText: "Select an adapter type",
3961
4010
  select: true,
3962
4011
  fullWidth: true,
3963
4012
  onChange: function onChange(event) {
@@ -3969,27 +4018,35 @@ var TrackAdapterSelector = /*#__PURE__*/observer(function (_ref2) {
3969
4018
  'data-testid': 'adapterTypeSelect'
3970
4019
  }
3971
4020
  }
3972
- }, getEnv(session).pluginManager.getElementTypesInGroup('adapter') // Exclude SNPCoverageAdapter from primary adapter user selection
4021
+ }, adapters // Excludes any adapter with the 'adapterMetadata.hiddenFromGUI' property, and anything with the 'adapterMetadata.category' property
3973
4022
  .filter(function (elt) {
3974
- return elt.name !== 'SNPCoverageAdapter';
4023
+ var _elt$adapterMetadata, _elt$adapterMetadata2;
4024
+
4025
+ return !((_elt$adapterMetadata = elt.adapterMetadata) !== null && _elt$adapterMetadata !== void 0 && _elt$adapterMetadata.hiddenFromGUI) && !((_elt$adapterMetadata2 = elt.adapterMetadata) !== null && _elt$adapterMetadata2 !== void 0 && _elt$adapterMetadata2.category);
3975
4026
  }).map(function (elt) {
3976
- return /*#__PURE__*/React.createElement(MenuItem$1, {
4027
+ var _elt$adapterMetadata3, _elt$adapterMetadata4;
4028
+
4029
+ return /*#__PURE__*/React.createElement(MenuItem, {
3977
4030
  key: elt.name,
3978
4031
  value: elt.name
3979
- }, elt.name);
3980
- }));
4032
+ }, (_elt$adapterMetadata3 = elt.adapterMetadata) !== null && _elt$adapterMetadata3 !== void 0 && _elt$adapterMetadata3.displayName ? (_elt$adapterMetadata4 = elt.adapterMetadata) === null || _elt$adapterMetadata4 === void 0 ? void 0 : _elt$adapterMetadata4.displayName : elt.name);
4033
+ }), categorizeAdapters(adapters.filter(function (elt) {
4034
+ var _elt$adapterMetadata5;
4035
+
4036
+ return !((_elt$adapterMetadata5 = elt.adapterMetadata) !== null && _elt$adapterMetadata5 !== void 0 && _elt$adapterMetadata5.hiddenFromGUI);
4037
+ })));
3981
4038
  });
3982
4039
 
3983
4040
  function UnknownAdapterPrompt(_ref3) {
3984
4041
  var model = _ref3.model;
3985
- var classes = useStyles$9();
3986
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Typography$1, {
4042
+ var classes = useStyles$8();
4043
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Typography, {
3987
4044
  className: classes.spacing
3988
- }, "Was not able to guess the adapter type for this data, but it may be in the list below. If not, you can", ' ', /*#__PURE__*/React.createElement(Link$1, {
4045
+ }, "JBrowse was not able to guess the adapter type for this data, but it may be in the list below. If not, you can", ' ', /*#__PURE__*/React.createElement(Link, {
3989
4046
  href: "https://github.com/GMOD/jbrowse-components/releases",
3990
4047
  target: "_blank",
3991
4048
  rel: "noopener noreferrer"
3992
- }, "check for new releases"), ' ', "of JBrowse to see if they support this data type or", ' ', /*#__PURE__*/React.createElement(Link$1, {
4049
+ }, "check for new releases"), ' ', "of JBrowse to see if they support this data type or", ' ', /*#__PURE__*/React.createElement(Link, {
3993
4050
  href: "https://github.com/GMOD/jbrowse-components/issues/new",
3994
4051
  target: "_blank",
3995
4052
  rel: "noopener noreferrer"
@@ -4000,14 +4057,15 @@ function UnknownAdapterPrompt(_ref3) {
4000
4057
 
4001
4058
  var TrackTypeSelector = /*#__PURE__*/observer(function (_ref4) {
4002
4059
  var model = _ref4.model;
4003
- var classes = useStyles$9();
4060
+ var classes = useStyles$8();
4004
4061
  var session = getSession(model);
4005
4062
  var trackType = model.trackType;
4063
+ var trackTypes = getTrackTypes(getEnv(session).pluginManager);
4006
4064
  return /*#__PURE__*/React.createElement(TextField, {
4007
4065
  className: classes.spacing,
4008
4066
  value: trackType,
4009
4067
  label: "trackType",
4010
- helperText: "A track type",
4068
+ helperText: "Select a track type",
4011
4069
  select: true,
4012
4070
  fullWidth: true,
4013
4071
  onChange: function onChange(event) {
@@ -4019,10 +4077,9 @@ var TrackTypeSelector = /*#__PURE__*/observer(function (_ref4) {
4019
4077
  'data-testid': 'trackTypeSelect'
4020
4078
  }
4021
4079
  }
4022
- }, getEnv(session).pluginManager.getElementTypesInGroup('track') // eslint-disable-next-line @typescript-eslint/no-explicit-any
4023
- .map(function (_ref5) {
4080
+ }, trackTypes.map(function (_ref5) {
4024
4081
  var name = _ref5.name;
4025
- return /*#__PURE__*/React.createElement(MenuItem$1, {
4082
+ return /*#__PURE__*/React.createElement(MenuItem, {
4026
4083
  key: name,
4027
4084
  value: name
4028
4085
  }, name);
@@ -4048,8 +4105,9 @@ var TrackAssemblySelector = /*#__PURE__*/observer(function (_ref6) {
4048
4105
  }
4049
4106
  }
4050
4107
  }, session.assemblies.map(function (conf) {
4051
- var name = readConfObject(conf, 'name');
4052
- return /*#__PURE__*/React.createElement(MenuItem$1, {
4108
+ return readConfObject(conf, 'name');
4109
+ }).map(function (name) {
4110
+ return /*#__PURE__*/React.createElement(MenuItem, {
4053
4111
  key: name,
4054
4112
  value: name
4055
4113
  }, name);
@@ -4058,20 +4116,21 @@ var TrackAssemblySelector = /*#__PURE__*/observer(function (_ref6) {
4058
4116
 
4059
4117
  function ConfirmTrack(_ref7) {
4060
4118
  var model = _ref7.model;
4061
- var classes = useStyles$9();
4119
+ var classes = useStyles$8();
4062
4120
  var trackName = model.trackName,
4063
4121
  trackAdapter = model.trackAdapter,
4064
4122
  trackType = model.trackType,
4065
- warningMessage = model.warningMessage;
4123
+ warningMessage = model.warningMessage,
4124
+ adapterHint = model.adapterHint;
4066
4125
 
4067
4126
  if (model.unsupported) {
4068
- return /*#__PURE__*/React.createElement(Typography$1, {
4127
+ return /*#__PURE__*/React.createElement(Typography, {
4069
4128
  className: classes.spacing
4070
- }, "This version of JBrowse cannot display data of this type. It is possible, however, that there is a newer version that can display them. You can", ' ', /*#__PURE__*/React.createElement(Link$1, {
4129
+ }, "This version of JBrowse cannot display data of this type. It is possible, however, that there is a newer version that can display them. You can", ' ', /*#__PURE__*/React.createElement(Link, {
4071
4130
  href: "https://github.com/GMOD/jbrowse-components/releases",
4072
4131
  target: "_blank",
4073
4132
  rel: "noopener noreferrer"
4074
- }, "check for new releases"), ' ', "of JBrowse or", ' ', /*#__PURE__*/React.createElement(Link$1, {
4133
+ }, "check for new releases"), ' ', "of JBrowse or", ' ', /*#__PURE__*/React.createElement(Link, {
4075
4134
  href: "https://github.com/GMOD/jbrowse-components/issues/new",
4076
4135
  target: "_blank",
4077
4136
  rel: "noopener noreferrer"
@@ -4084,14 +4143,18 @@ function ConfirmTrack(_ref7) {
4084
4143
  });
4085
4144
  }
4086
4145
 
4146
+ if (adapterHint === '' && trackAdapter) {
4147
+ model.setAdapterHint(trackAdapter.type);
4148
+ }
4149
+
4087
4150
  if (!(trackAdapter !== null && trackAdapter !== void 0 && trackAdapter.type)) {
4088
- return /*#__PURE__*/React.createElement(Typography$1, null, "Could not recognize this data type.");
4151
+ return /*#__PURE__*/React.createElement(Typography, null, "Could not recognize this data type.");
4089
4152
  }
4090
4153
 
4091
4154
  return /*#__PURE__*/React.createElement("div", null, trackAdapter ? /*#__PURE__*/React.createElement(StatusMessage, {
4092
4155
  trackAdapter: trackAdapter,
4093
4156
  trackType: trackType
4094
- }) : null, warningMessage ? /*#__PURE__*/React.createElement(Typography$1, {
4157
+ }) : null, warningMessage ? /*#__PURE__*/React.createElement(Typography, {
4095
4158
  style: {
4096
4159
  color: 'orange'
4097
4160
  }
@@ -4118,7 +4181,7 @@ function ConfirmTrack(_ref7) {
4118
4181
 
4119
4182
  var ConfirmTrack$1 = /*#__PURE__*/observer(ConfirmTrack);
4120
4183
 
4121
- var useStyles$a = /*#__PURE__*/makeStyles(function (theme) {
4184
+ var useStyles$9 = /*#__PURE__*/makeStyles(function (theme) {
4122
4185
  return {
4123
4186
  paper: {
4124
4187
  display: 'flex',
@@ -4133,7 +4196,7 @@ var useStyles$a = /*#__PURE__*/makeStyles(function (theme) {
4133
4196
 
4134
4197
  function TrackSourceSelect(_ref) {
4135
4198
  var model = _ref.model;
4136
- var classes = useStyles$a();
4199
+ var classes = useStyles$9();
4137
4200
  var rootModel = getRoot(model);
4138
4201
  return /*#__PURE__*/React.createElement(Paper, {
4139
4202
  className: classes.paper
@@ -4158,7 +4221,7 @@ function TrackSourceSelect(_ref) {
4158
4221
 
4159
4222
  var TrackSourceSelect$1 = /*#__PURE__*/observer(TrackSourceSelect);
4160
4223
 
4161
- var useStyles$b = /*#__PURE__*/makeStyles$1(function (theme) {
4224
+ var useStyles$a = /*#__PURE__*/makeStyles(function (theme) {
4162
4225
  return {
4163
4226
  root: {
4164
4227
  marginTop: theme.spacing(1)
@@ -4191,7 +4254,7 @@ function AddTrackWidget(_ref) {
4191
4254
  activeStep = _useState2[0],
4192
4255
  setActiveStep = _useState2[1];
4193
4256
 
4194
- var classes = useStyles$b();
4257
+ var classes = useStyles$a();
4195
4258
  var session = getSession(model);
4196
4259
  var assembly = model.assembly,
4197
4260
  trackAdapter = model.trackAdapter,
@@ -4371,7 +4434,7 @@ function ConnectionTypeSelect(props) {
4371
4434
 
4372
4435
  return /*#__PURE__*/React.createElement("form", {
4373
4436
  autoComplete: "off"
4374
- }, /*#__PURE__*/React.createElement(TextField, {
4437
+ }, /*#__PURE__*/React.createElement(TextField$1, {
4375
4438
  value: connectionType.name,
4376
4439
  label: "connectionType",
4377
4440
  helperText: connectionType.description ? /*#__PURE__*/React.createElement(React.Fragment, null, connectionType.description, connectionType.url ? /*#__PURE__*/React.createElement(IconButton$1, {
@@ -4392,7 +4455,7 @@ function ConnectionTypeSelect(props) {
4392
4455
  })));
4393
4456
  }
4394
4457
 
4395
- var useStyles$c = /*#__PURE__*/makeStyles(function (theme) {
4458
+ var useStyles$b = /*#__PURE__*/makeStyles$1(function (theme) {
4396
4459
  return {
4397
4460
  root: {
4398
4461
  marginTop: theme.spacing(1)
@@ -4434,7 +4497,7 @@ function AddConnectionWidget(_ref) {
4434
4497
  activeStep = _useState8[0],
4435
4498
  setActiveStep = _useState8[1];
4436
4499
 
4437
- var classes = useStyles$c();
4500
+ var classes = useStyles$b();
4438
4501
  var session = getSession(model);
4439
4502
 
4440
4503
  var _getEnv = getEnv(session),
@@ -4607,7 +4670,7 @@ var DeleteConnectionDialog$3 = {
4607
4670
  'default': DeleteConnectionDialog$2
4608
4671
  };
4609
4672
 
4610
- var useStyles$d = /*#__PURE__*/makeStyles$1(function (theme) {
4673
+ var useStyles$c = /*#__PURE__*/makeStyles(function (theme) {
4611
4674
  return {
4612
4675
  closeButton: {
4613
4676
  position: 'absolute',
@@ -4626,7 +4689,7 @@ function ManageConnectionsDlg(_ref) {
4626
4689
  var session = _ref.session,
4627
4690
  handleClose = _ref.handleClose,
4628
4691
  breakConnection = _ref.breakConnection;
4629
- var classes = useStyles$d();
4692
+ var classes = useStyles$c();
4630
4693
  var adminMode = session.adminMode,
4631
4694
  connections = session.connections,
4632
4695
  sessionConnections = session.sessionConnections;
@@ -4672,7 +4735,7 @@ var ManageConnectionsDialog$2 = {
4672
4735
  'default': ManageConnectionsDialog$1
4673
4736
  };
4674
4737
 
4675
- var useStyles$e = /*#__PURE__*/makeStyles$1(function (theme) {
4738
+ var useStyles$d = /*#__PURE__*/makeStyles(function (theme) {
4676
4739
  return {
4677
4740
  closeButton: {
4678
4741
  position: 'absolute',
@@ -4692,7 +4755,7 @@ function ToggleConnectionDialog(_ref) {
4692
4755
  handleClose = _ref.handleClose,
4693
4756
  assemblyName = _ref.assemblyName,
4694
4757
  breakConnection = _ref.breakConnection;
4695
- var classes = useStyles$e();
4758
+ var classes = useStyles$d();
4696
4759
  var connections = session.connections,
4697
4760
  connectionInstances = session.connectionInstances;
4698
4761
  var assemblySpecificConnections = connections.filter(function (c) {