@jbrowse/product-core 4.3.0 → 5.0.0-beta.10

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 (157) hide show
  1. package/esm/RootModel/BaseRootModel.d.ts +514 -323
  2. package/esm/RootModel/BaseRootModel.js +141 -14
  3. package/esm/RootModel/HierarchicalConfig.d.ts +30 -20
  4. package/esm/RootModel/HierarchicalConfig.js +36 -0
  5. package/esm/RootModel/InternetAccounts.d.ts +18 -1
  6. package/esm/RootModel/InternetAccounts.js +120 -22
  7. package/esm/RootModel/PreferencesConfig.d.ts +37 -0
  8. package/esm/RootModel/PreferencesConfig.js +75 -0
  9. package/esm/RootModel/createConfigModel.d.ts +257 -0
  10. package/esm/RootModel/createConfigModel.js +77 -0
  11. package/esm/RootModel/index.d.ts +10 -5
  12. package/esm/RootModel/index.js +10 -5
  13. package/esm/RootModel/menuItems.d.ts +24 -0
  14. package/esm/RootModel/menuItems.js +141 -0
  15. package/esm/Session/BaseSession.d.ts +285 -11
  16. package/esm/Session/BaseSession.js +383 -15
  17. package/esm/Session/Connections.d.ts +115 -8
  18. package/esm/Session/Connections.js +196 -74
  19. package/esm/Session/DrawerWidgets.d.ts +145 -13
  20. package/esm/Session/DrawerWidgets.js +219 -49
  21. package/esm/Session/MultipleViews.d.ts +1067 -1177
  22. package/esm/Session/MultipleViews.js +395 -64
  23. package/esm/Session/Preferences.d.ts +1389 -0
  24. package/esm/Session/Preferences.js +58 -0
  25. package/esm/Session/ReferenceManagement.d.ts +28 -3
  26. package/esm/Session/ReferenceManagement.js +56 -41
  27. package/esm/Session/SessionTracks.d.ts +1101 -1180
  28. package/esm/Session/SessionTracks.js +584 -34
  29. package/esm/Session/Themes.d.ts +136 -2
  30. package/esm/Session/Themes.js +267 -21
  31. package/esm/Session/TrackMenu.d.ts +88 -0
  32. package/esm/Session/TrackMenu.js +175 -0
  33. package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
  34. package/esm/Session/TrackMenuSessionMixin.js +42 -0
  35. package/esm/Session/Tracks.d.ts +945 -1159
  36. package/esm/Session/Tracks.js +229 -30
  37. package/esm/Session/assertSessionModel.d.ts +21 -0
  38. package/esm/Session/assertSessionModel.js +1 -0
  39. package/esm/Session/copyTrackSnapshot.d.ts +21 -0
  40. package/esm/Session/copyTrackSnapshot.js +20 -0
  41. package/esm/Session/developerMode.d.ts +7 -0
  42. package/esm/Session/developerMode.js +43 -0
  43. package/esm/Session/finalizeSession.d.ts +15 -0
  44. package/esm/Session/finalizeSession.js +28 -0
  45. package/esm/Session/hydratedForms.d.ts +16 -0
  46. package/esm/Session/hydratedForms.js +13 -0
  47. package/esm/Session/index.d.ts +27 -10
  48. package/esm/Session/index.js +16 -10
  49. package/esm/Session/pluginExtraTrackItems.d.ts +11 -0
  50. package/esm/Session/pluginExtraTrackItems.js +13 -0
  51. package/esm/Session/readdedTrackConf.d.ts +8 -0
  52. package/esm/Session/readdedTrackConf.js +24 -0
  53. package/esm/Session/shareableSnapshot.d.ts +23 -0
  54. package/esm/Session/shareableSnapshot.js +66 -0
  55. package/esm/Session/temporaryAssemblyTracks.d.ts +24 -0
  56. package/esm/Session/temporaryAssemblyTracks.js +37 -0
  57. package/esm/assertExtends.d.ts +55 -0
  58. package/esm/assertExtends.js +1 -0
  59. package/esm/controllerTracks.d.ts +76 -0
  60. package/esm/controllerTracks.js +125 -0
  61. package/esm/destroyViewState.d.ts +21 -0
  62. package/esm/destroyViewState.js +29 -0
  63. package/esm/embeddedController.d.ts +57 -0
  64. package/esm/embeddedController.js +100 -0
  65. package/esm/index.d.ts +32 -4
  66. package/esm/index.js +31 -4
  67. package/esm/loadPlugins.d.ts +38 -0
  68. package/esm/loadPlugins.js +45 -0
  69. package/esm/localFiles.d.ts +74 -0
  70. package/esm/localFiles.js +156 -0
  71. package/esm/observeSession.d.ts +55 -0
  72. package/esm/observeSession.js +90 -0
  73. package/esm/parallelChunkLoading.d.ts +30 -0
  74. package/esm/parallelChunkLoading.js +71 -0
  75. package/esm/pluginInput.d.ts +32 -0
  76. package/esm/pluginInput.js +19 -0
  77. package/esm/pruneUnbuildableNodes.d.ts +109 -0
  78. package/esm/pruneUnbuildableNodes.js +562 -0
  79. package/esm/resolveAssemblies.d.ts +68 -0
  80. package/esm/resolveAssemblies.js +77 -0
  81. package/esm/rpcWorker.d.ts +36 -4
  82. package/esm/rpcWorker.js +126 -20
  83. package/esm/sessionMigrations/index.d.ts +15 -0
  84. package/esm/sessionMigrations/index.js +178 -0
  85. package/esm/sessionUrl.d.ts +40 -0
  86. package/esm/sessionUrl.js +66 -0
  87. package/esm/sessionUtils.d.ts +74 -0
  88. package/esm/sessionUtils.js +599 -0
  89. package/esm/siblingCast.d.ts +14 -0
  90. package/esm/siblingCast.js +16 -0
  91. package/esm/snapshotUtils.d.ts +8 -0
  92. package/esm/snapshotUtils.js +12 -0
  93. package/esm/ui/AboutDialog.d.ts +8 -3
  94. package/esm/ui/AboutDialog.js +14 -5
  95. package/esm/ui/AboutDialogContents.d.ts +2 -6
  96. package/esm/ui/AboutDialogContents.js +13 -17
  97. package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
  98. package/esm/ui/AssemblyInfoPanel.js +63 -0
  99. package/esm/ui/DescriptionPanel.d.ts +3 -0
  100. package/esm/ui/DescriptionPanel.js +25 -0
  101. package/esm/ui/Drawer.d.ts +21 -0
  102. package/esm/ui/Drawer.js +60 -0
  103. package/esm/ui/DrawerControls.d.ts +5 -0
  104. package/esm/ui/DrawerControls.js +28 -0
  105. package/esm/ui/DrawerHeader.d.ts +6 -0
  106. package/esm/ui/DrawerHeader.js +36 -0
  107. package/esm/ui/DrawerWidget.d.ts +5 -0
  108. package/esm/ui/DrawerWidget.js +12 -0
  109. package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
  110. package/esm/ui/DrawerWidgetSelector.js +40 -0
  111. package/esm/ui/FileInfoPanel.d.ts +3 -6
  112. package/esm/ui/FileInfoPanel.js +36 -26
  113. package/esm/ui/GeneralPreferencesTab.d.ts +17 -0
  114. package/esm/ui/GeneralPreferencesTab.js +34 -0
  115. package/esm/ui/HeaderButtons.d.ts +2 -1
  116. package/esm/ui/HeaderButtons.js +15 -17
  117. package/esm/ui/ModalWidget.d.ts +13 -0
  118. package/esm/ui/ModalWidget.js +48 -0
  119. package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
  120. package/esm/ui/ModalWidgetAppBar.js +9 -0
  121. package/esm/ui/PreferenceCheckbox.d.ts +6 -0
  122. package/esm/ui/PreferenceCheckbox.js +16 -0
  123. package/esm/ui/PreferencesDialog.d.ts +34 -0
  124. package/esm/ui/PreferencesDialog.js +55 -0
  125. package/esm/ui/PreferencesResetDialog.d.ts +8 -0
  126. package/esm/ui/PreferencesResetDialog.js +19 -0
  127. package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
  128. package/esm/ui/RefNameAliasesDialog.js +65 -0
  129. package/esm/ui/RefNameInfoDialog.d.ts +3 -6
  130. package/esm/ui/RefNameInfoDialog.js +45 -63
  131. package/esm/ui/ViewPreferencesTab.d.ts +12 -0
  132. package/esm/ui/ViewPreferencesTab.js +15 -0
  133. package/esm/ui/WidgetBody.d.ts +18 -0
  134. package/esm/ui/WidgetBody.js +22 -0
  135. package/esm/ui/WidgetHeading.d.ts +7 -0
  136. package/esm/ui/WidgetHeading.js +7 -0
  137. package/esm/ui/aboutTestUtils.d.ts +140 -0
  138. package/esm/ui/aboutTestUtils.js +25 -0
  139. package/esm/ui/descriptionHtml.d.ts +25 -0
  140. package/esm/ui/descriptionHtml.js +64 -0
  141. package/esm/ui/drawerLayout.d.ts +18 -0
  142. package/esm/ui/drawerLayout.js +22 -0
  143. package/esm/ui/index.d.ts +15 -1
  144. package/esm/ui/index.js +13 -1
  145. package/esm/ui/preferencesReset.d.ts +18 -0
  146. package/esm/ui/preferencesReset.js +69 -0
  147. package/esm/ui/util.d.ts +22 -7
  148. package/esm/ui/util.js +19 -41
  149. package/esm/useEngineLifecycle.d.ts +19 -0
  150. package/esm/useEngineLifecycle.js +44 -0
  151. package/package.json +15 -18
  152. package/esm/RootModel/FormatAbout.d.ts +0 -12
  153. package/esm/RootModel/FormatAbout.js +0 -15
  154. package/esm/RootModel/FormatDetails.d.ts +0 -24
  155. package/esm/RootModel/FormatDetails.js +0 -27
  156. package/esm/Session/DialogQueue.d.ts +0 -17
  157. package/esm/Session/DialogQueue.js +0 -31
@@ -0,0 +1,13 @@
1
+ import type { SessionWithWidgets } from '@jbrowse/core/util';
2
+ /**
3
+ * Renders the session's visible widget in a modal dialog rather than a drawer.
4
+ * `onClose` is caller-owned: in the app the modal is a pop-out of the drawer and
5
+ * closing returns the widget to it, while in embedded products the modal is the
6
+ * only widget surface and closing must dismiss the widget outright.
7
+ */
8
+ declare const ModalWidget: ({ session, onClose, fullScreen, }: {
9
+ session: SessionWithWidgets;
10
+ onClose: () => void;
11
+ fullScreen?: boolean;
12
+ }) => import("react").JSX.Element | null;
13
+ export default ModalWidget;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Dialog } from '@jbrowse/core/ui';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { getEnv } from '@jbrowse/mobx-state-tree';
5
+ import { Paper } from '@mui/material';
6
+ import { observer } from 'mobx-react';
7
+ import ModalWidgetAppBar from './ModalWidgetAppBar.js';
8
+ import WidgetBody from './WidgetBody.js';
9
+ const useStyles = makeStyles()({
10
+ paper: {
11
+ overflow: 'auto',
12
+ // the track selector's fab positions against this
13
+ position: 'relative',
14
+ width: 800,
15
+ maxWidth: '100%',
16
+ // matches the drawer's sense of "tall but not full screen", and unlike a
17
+ // window.innerHeight read it keeps up with a window resize
18
+ height: '62.5vh',
19
+ },
20
+ fullScreenDialog: {
21
+ '& > .MuiScopedCssBaseline-root': {
22
+ display: 'flex',
23
+ flexDirection: 'column',
24
+ flex: 1,
25
+ minHeight: 0,
26
+ },
27
+ },
28
+ // the widget takes whatever the dialog's app bar leaves
29
+ fullScreenPaper: {
30
+ flex: 1,
31
+ minHeight: 0,
32
+ width: '100%',
33
+ height: 'auto',
34
+ },
35
+ });
36
+ /**
37
+ * Renders the session's visible widget in a modal dialog rather than a drawer.
38
+ * `onClose` is caller-owned: in the app the modal is a pop-out of the drawer and
39
+ * closing returns the widget to it, while in embedded products the modal is the
40
+ * only widget surface and closing must dismiss the widget outright.
41
+ */
42
+ const ModalWidget = observer(function ModalWidget({ session, onClose, fullScreen = false, }) {
43
+ const { classes, cx } = useStyles();
44
+ const { visibleWidget } = session;
45
+ const { pluginManager } = getEnv(session);
46
+ return visibleWidget ? (_jsx(Dialog, { open: true, onClose: onClose, maxWidth: "xl", fullScreen: fullScreen, classes: fullScreen ? { paper: classes.fullScreenDialog } : undefined, header: _jsx(ModalWidgetAppBar, { widget: visibleWidget, pluginManager: pluginManager, onClose: onClose }), children: _jsx(Paper, { className: cx(classes.paper, fullScreen && classes.fullScreenPaper), children: _jsx(WidgetBody, { session: session, widget: visibleWidget }) }) })) : null;
47
+ });
48
+ export default ModalWidget;
@@ -0,0 +1,8 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ import type { Widget } from '@jbrowse/core/util';
3
+ declare const ModalWidgetAppBar: ({ widget, pluginManager, onClose, }: {
4
+ widget: Widget;
5
+ pluginManager: PluginManager;
6
+ onClose: () => void;
7
+ }) => import("react").JSX.Element;
8
+ export default ModalWidgetAppBar;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import CloseIcon from '@mui/icons-material/Close';
3
+ import { AppBar, Box, IconButton, Toolbar } from '@mui/material';
4
+ import { observer } from 'mobx-react';
5
+ import WidgetHeading from './WidgetHeading.js';
6
+ const ModalWidgetAppBar = observer(function ModalWidgetAppBar({ widget, pluginManager, onClose, }) {
7
+ return (_jsx(AppBar, { position: "static", children: _jsxs(Toolbar, { children: [_jsx(WidgetHeading, { widget: widget, pluginManager: pluginManager }), _jsx(Box, { sx: { flexGrow: 1 } }), _jsx(IconButton, { color: "inherit", onClick: onClose, "data-testid": "modal-close", children: _jsx(CloseIcon, {}) })] }) }));
8
+ });
9
+ export default ModalWidgetAppBar;
@@ -0,0 +1,6 @@
1
+ export default function PreferenceCheckbox({ checked, label, help, onChange, }: {
2
+ checked: boolean;
3
+ label: string;
4
+ help?: string;
5
+ onChange: (checked: boolean) => void;
6
+ }): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { LabeledCheckbox } from '@jbrowse/core/ui';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { Typography } from '@mui/material';
5
+ const useStyles = makeStyles()({
6
+ row: {
7
+ alignItems: 'flex-start',
8
+ marginTop: 8,
9
+ },
10
+ });
11
+ export default function PreferenceCheckbox({ checked, label, help, onChange, }) {
12
+ const { classes } = useStyles();
13
+ return (_jsx(LabeledCheckbox, { className: classes.row, checked: checked, onChange: checked => {
14
+ onChange(checked);
15
+ }, label: _jsxs(_Fragment, { children: [_jsx(Typography, { children: label }), help ? (_jsx(Typography, { variant: "body2", color: "text.secondary", children: help })) : null] }) }));
16
+ }
@@ -0,0 +1,34 @@
1
+ import type { GeneralPreferencesSession } from './GeneralPreferencesTab.tsx';
2
+ import type { ViewPreferencesSession } from './ViewPreferencesTab.tsx';
3
+ import type { ResettablePreferencesSession } from './preferencesReset.ts';
4
+ import type PluginManager from '@jbrowse/core/PluginManager';
5
+ import type React from 'react';
6
+ export interface PreferencesDialogSession extends GeneralPreferencesSession, ViewPreferencesSession, ResettablePreferencesSession {
7
+ }
8
+ /**
9
+ * Descriptor returned from the `Core-preferencesDialogPanels` extension point.
10
+ * Each panel renders as its own tab in the dialog, labeled by `name`.
11
+ */
12
+ export interface PreferencesPanelDescriptor {
13
+ name: string;
14
+ Component: React.ComponentType<{
15
+ session: PreferencesDialogSession;
16
+ }>;
17
+ }
18
+ declare module '@jbrowse/core/PluginManager' {
19
+ interface ExtensionPointRegistry {
20
+ 'Core-preferencesDialogPanels': {
21
+ args: PreferencesPanelDescriptor[];
22
+ result: PreferencesPanelDescriptor[];
23
+ props: {
24
+ session: PreferencesDialogSession;
25
+ };
26
+ };
27
+ }
28
+ }
29
+ declare const PreferencesDialog: ({ handleClose, session, pluginManager, }: {
30
+ handleClose: () => void;
31
+ session: PreferencesDialogSession;
32
+ pluginManager: PluginManager;
33
+ }) => React.JSX.Element;
34
+ export default PreferencesDialog;
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Suspense, useState } from 'react';
3
+ import { Dialog } from '@jbrowse/core/ui';
4
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
5
+ import { Button, DialogActions, DialogContent, Tab, Tabs } from '@mui/material';
6
+ import { observer } from 'mobx-react';
7
+ import GeneralPreferencesTab from './GeneralPreferencesTab.js';
8
+ import PreferencesResetDialog from './PreferencesResetDialog.js';
9
+ import ViewPreferencesTab from './ViewPreferencesTab.js';
10
+ import { collectPreferenceChanges, resetAllPreferences, resetPreferenceChange, } from './preferencesReset.js';
11
+ const useStyles = makeStyles()({
12
+ container: {
13
+ width: 800,
14
+ minHeight: 300,
15
+ },
16
+ });
17
+ const PreferencesDialog = observer(function PreferencesDialog({ handleClose, session, pluginManager, }) {
18
+ const { classes } = useStyles();
19
+ const [tab, setTab] = useState('general');
20
+ const [resetDialogOpen, setResetDialogOpen] = useState(false);
21
+ const pluginPanels = pluginManager.evaluateExtensionPoint(
22
+ /** #extensionPoint Core-preferencesDialogPanels | sync | Add tabs to the preferences dialog */
23
+ 'Core-preferencesDialogPanels', [], { session });
24
+ const tabs = [
25
+ {
26
+ value: 'general',
27
+ label: 'General',
28
+ content: _jsx(GeneralPreferencesTab, { session: session }),
29
+ },
30
+ {
31
+ value: 'views',
32
+ label: 'Views',
33
+ content: _jsx(ViewPreferencesTab, { session: session }),
34
+ },
35
+ ...pluginPanels.map(({ name, Component }) => ({
36
+ value: `plugin:${name}`,
37
+ label: name,
38
+ content: (_jsx(Suspense, { fallback: null, children: _jsx(Component, { session: session }) })),
39
+ })),
40
+ ];
41
+ return (_jsxs(Dialog, { title: "Preferences", open: true, onClose: handleClose, maxWidth: "xl", children: [_jsx(Tabs, { value: tab, variant: "scrollable", scrollButtons: "auto", onChange: (_, value) => {
42
+ setTab(value);
43
+ }, children: tabs.map(t => (_jsx(Tab, { value: t.value, label: t.label }, t.value))) }), _jsx(DialogContent, { className: classes.container, children: tabs.find(t => t.value === tab)?.content }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
44
+ setResetDialogOpen(true);
45
+ }, children: "Reset to defaults\u2026" }), _jsx(Button, { variant: "contained", onClick: () => {
46
+ handleClose();
47
+ }, children: "Close" })] }), resetDialogOpen ? (_jsx(PreferencesResetDialog, { changes: collectPreferenceChanges(session), onReset: () => {
48
+ resetAllPreferences(session);
49
+ }, onResetRow: change => {
50
+ resetPreferenceChange(session, change);
51
+ }, onClose: () => {
52
+ setResetDialogOpen(false);
53
+ } })) : null] }));
54
+ });
55
+ export default PreferencesDialog;
@@ -0,0 +1,8 @@
1
+ import type { TrackConfigChange } from '@jbrowse/core/util';
2
+ declare const PreferencesResetDialog: ({ changes, onReset, onResetRow, onClose, }: {
3
+ changes: TrackConfigChange[];
4
+ onReset: () => void;
5
+ onResetRow: (change: TrackConfigChange) => void;
6
+ onClose: () => void;
7
+ }) => import("react").JSX.Element;
8
+ export default PreferencesResetDialog;
@@ -0,0 +1,19 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SettingsChangesTable, SubmitDialog } from '@jbrowse/core/ui';
3
+ import { DialogContentText, Typography } from '@mui/material';
4
+ import { observer } from 'mobx-react';
5
+ // Confirmation shown before "Reset to defaults" wipes every customized
6
+ // preference. Lists exactly what will change as a Setting/Default/Current diff
7
+ // (the same table the per-track reset uses) so the reset is never a blind
8
+ // action. A standard modal — focus-trapped, Enter to confirm, Escape to cancel
9
+ // — replaces the former label-swapping two-click button, which screen readers
10
+ // and keyboard users could not follow.
11
+ const PreferencesResetDialog = observer(function PreferencesResetDialog({ changes, onReset, onResetRow, onClose, }) {
12
+ return (_jsx(SubmitDialog, { open: true, title: "Reset preferences to defaults", maxWidth: "md", submitText: "Reset to defaults", submitDisabled: changes.length === 0, cancelText: "Cancel", onCancel: () => {
13
+ onClose();
14
+ }, onSubmit: () => {
15
+ onReset();
16
+ onClose();
17
+ }, children: changes.length ? (_jsxs(_Fragment, { children: [_jsx(DialogContentText, { children: "These preferences differ from their defaults. Reset an individual entry with its revert button, or reset them all below:" }), _jsx(SettingsChangesTable, { changes: changes, onResetRow: onResetRow })] })) : (_jsx(Typography, { children: "All preferences are already at their defaults." })) }));
18
+ });
19
+ export default PreferencesResetDialog;
@@ -0,0 +1,9 @@
1
+ import type { AbstractSessionModel } from '@jbrowse/core/util';
2
+ export declare function formatRows(rows: [string, string[]][]): string;
3
+ export declare function alignRows(rows: [string, string[]][]): string;
4
+ declare const RefNameAliasesDialog: ({ assemblyName, session, onClose, }: {
5
+ assemblyName: string;
6
+ session: AbstractSessionModel;
7
+ onClose: () => void;
8
+ }) => import("react").JSX.Element;
9
+ export default RefNameAliasesDialog;
@@ -0,0 +1,65 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { CopyToClipboardButton, Dialog, ErrorBanner, LoadingEllipses, } from '@jbrowse/core/ui';
4
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
5
+ import { DialogContent, TextField, Typography } from '@mui/material';
6
+ import { observer } from 'mobx-react';
7
+ const MAX_ROWS = 10_000;
8
+ const MAX_NAME_COLUMN = 30;
9
+ const useStyles = makeStyles()(theme => ({
10
+ container: {
11
+ minWidth: 800,
12
+ },
13
+ rows: {
14
+ maxHeight: 300,
15
+ overflow: 'auto',
16
+ background: theme.palette.background.default,
17
+ },
18
+ header: {
19
+ display: 'flex',
20
+ alignItems: 'center',
21
+ gap: theme.spacing(2),
22
+ marginBottom: theme.spacing(1),
23
+ },
24
+ }));
25
+ // what the copy button puts on the clipboard: tab-separated, so it pastes into
26
+ // a spreadsheet as two columns
27
+ export function formatRows(rows) {
28
+ return rows
29
+ .map(([refName, names]) => `${refName}\t${names.slice(1).join(', ')}`)
30
+ .join('\n');
31
+ }
32
+ // what the dialog shows. A tab is 8 columns wide in a <pre>, so an assembly
33
+ // whose names straddle that (hg38's `chr7` and `chr7_GL383534v2_alt`) reads as
34
+ // two ragged columns; padding to the widest name on screen is one. Capped, or
35
+ // a single 60-character GenArk contig indents every alias off the right edge
36
+ export function alignRows(rows) {
37
+ let width = 0;
38
+ for (const [refName] of rows) {
39
+ width = Math.max(width, refName.length);
40
+ }
41
+ const pad = Math.min(width, MAX_NAME_COLUMN);
42
+ return rows
43
+ .map(([refName, names]) => `${refName.padEnd(pad)} ${names.slice(1).join(', ')}`)
44
+ .join('\n');
45
+ }
46
+ const RefNameAliasesDialog = observer(function RefNameAliasesDialog({ assemblyName, session, onClose, }) {
47
+ const { classes } = useStyles();
48
+ const [filter, setFilter] = useState('');
49
+ const assembly = session.assemblyManager.get(assemblyName);
50
+ // reading this is also what starts the load: assembly.afterAttach kicks
51
+ // load() when refNameAliases is first observed, so the dialog fills in for an
52
+ // assembly nothing has looked at yet instead of showing an empty table
53
+ const names = assembly?.namesByCanonicalRefName;
54
+ const query = filter.toLowerCase();
55
+ const rows = names
56
+ ? [...names].filter(([, group]) => group.some(name => name.toLowerCase().includes(query)))
57
+ : [];
58
+ const truncated = rows.length > MAX_ROWS;
59
+ return (_jsx(Dialog, { open: true, title: `Reference name aliases for ${assemblyName}`, onClose: () => {
60
+ onClose();
61
+ }, maxWidth: "xl", children: _jsx(DialogContent, { className: classes.container, children: assembly?.error ? (_jsx(ErrorBanner, { error: assembly.error })) : !assembly ? (_jsxs(Typography, { children: ["No assembly named ", assemblyName, " is loaded in this session"] })) : !names ? (_jsx(LoadingEllipses, { message: "Loading assembly" })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: classes.header, children: [_jsx(TextField, { label: "Filter", helperText: "Matches a reference name or any of its aliases", value: filter, onChange: event => {
62
+ setFilter(event.target.value);
63
+ } }), _jsx(CopyToClipboardButton, { variant: "contained", value: () => formatRows(rows), children: "Copy ref name aliases" })] }), _jsx("pre", { className: classes.rows, children: alignRows(truncated ? rows.slice(0, MAX_ROWS) : rows) }), truncated ? (_jsxs(Typography, { variant: "caption", children: ["Showing the first ", MAX_ROWS, " of ", rows.length, " reference names; filter, or use the copy button, for the rest"] })) : null] })) }) }));
64
+ });
65
+ export default RefNameAliasesDialog;
@@ -1,8 +1,5 @@
1
- import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
2
- import type { AbstractSessionModel } from '@jbrowse/core/util';
3
- declare const RefNameInfoDialog: ({ config, session, onClose, }: {
4
- config: AnyConfigurationModel | Record<string, unknown>;
5
- session: AbstractSessionModel;
1
+ import type { AboutPanelProps } from './util.ts';
2
+ declare const RefNameInfoDialog: ({ config, session, onClose, }: AboutPanelProps & {
6
3
  onClose: () => void;
7
- }) => import("react/jsx-runtime").JSX.Element;
4
+ }) => import("react").JSX.Element;
8
5
  export default RefNameInfoDialog;
@@ -1,10 +1,12 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
3
- import { Dialog, ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { readConfObject } from '@jbrowse/core/configuration';
3
+ import { CopyToClipboardButton, Dialog, ErrorBanner, LoadingEllipses, } from '@jbrowse/core/ui';
4
+ import { createStatusFanOut, statusProgressLabel } from '@jbrowse/core/util';
5
+ import { getConfAssemblyNames, getConfRpcSessionId, } from '@jbrowse/core/util/tracks';
4
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
5
- import { Button, DialogContent } from '@mui/material';
7
+ import { useFetch } from '@jbrowse/core/util/useFetch';
8
+ import { DialogContent } from '@mui/material';
6
9
  import { observer } from 'mobx-react';
7
- import { readConf } from "./util.js";
8
10
  const MAX_REF_NAMES = 10_000;
9
11
  const useStyles = makeStyles()(theme => ({
10
12
  container: {
@@ -17,66 +19,46 @@ const useStyles = makeStyles()(theme => ({
17
19
  background: theme.palette.background.default,
18
20
  },
19
21
  }));
22
+ function formatRefNames(data, truncate) {
23
+ return data
24
+ .flatMap(([assemblyName, names]) => [
25
+ `--- ${assemblyName} ---`,
26
+ ...(truncate ? names.slice(0, MAX_REF_NAMES) : names),
27
+ truncate && names.length > MAX_REF_NAMES
28
+ ? `\nToo many refNames to show in browser for ${assemblyName}, use "Copy ref names" button to copy to clipboard`
29
+ : '',
30
+ ])
31
+ .filter(s => s !== '')
32
+ .join('\n');
33
+ }
20
34
  const RefNameInfoDialog = observer(function RefNameInfoDialog({ config, session, onClose, }) {
21
35
  const { classes } = useStyles();
22
- const [error, setError] = useState();
23
- const [refNames, setRefNames] = useState();
24
- const [copied, setCopied] = useState(false);
25
36
  const { rpcManager } = session;
26
- const trackId = readConf(config, 'trackId');
27
- const assemblyNames = readConf(config, 'assemblyNames');
28
- useEffect(() => {
29
- ;
30
- (async () => {
31
- try {
32
- const map = await Promise.all([...new Set(assemblyNames)].map(async (assemblyName) => {
33
- const adapterConfig = readConf(config, 'adapter');
34
- return [
35
- assemblyName,
36
- (await rpcManager.call(trackId, 'CoreGetRefNames', {
37
- adapterConfig,
38
- regions: [
39
- {
40
- assemblyName,
41
- },
42
- ],
43
- })),
44
- ];
45
- }));
46
- setRefNames(Object.fromEntries(map));
47
- }
48
- catch (e) {
49
- console.error(e);
50
- setError(e);
51
- }
52
- })();
53
- }, [config, rpcManager, trackId, assemblyNames]);
54
- const names = refNames ? Object.entries(refNames) : [];
55
- const result = names
56
- .flatMap(([assemblyName, refNames]) => {
57
- return [
58
- `--- ${assemblyName} ---`,
59
- ...refNames.slice(0, MAX_REF_NAMES),
60
- refNames.length > MAX_REF_NAMES
61
- ? `\nToo many refNames to show in browser for ${assemblyName}, use "Copy ref names" button to copy to clipboard`
62
- : '',
63
- ];
64
- })
65
- .filter(f => !!f)
66
- .join('\n');
67
- return (_jsx(Dialog, { open: true, title: "Reference sequence names used in track", onClose: onClose, maxWidth: "xl", children: _jsx(DialogContent, { className: classes.container, children: error ? (_jsx(ErrorMessage, { error: error })) : refNames === undefined ? (_jsx(LoadingEllipses, { message: "Loading refNames" })) : (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "contained", onClick: async () => {
68
- const { default: copy } = await import('copy-to-clipboard');
69
- copy(names
70
- .flatMap(([assemblyName, refNames]) => [
71
- `--- ${assemblyName} ---`,
72
- ...refNames,
73
- ])
74
- .filter(f => !!f)
75
- .join('\n'));
76
- setCopied(true);
77
- setTimeout(() => {
78
- setCopied(false);
79
- }, 1000);
80
- }, children: copied ? 'Copied to clipboard!' : 'Copy ref names' }), _jsx("pre", { className: classes.refNames, children: result })] })) }) }));
37
+ const adapterConfig = readConfObject(config, 'adapter');
38
+ const sessionId = getConfRpcSessionId(config);
39
+ const { data, error, isLoading, status } = useFetch(['CoreGetRefNames', sessionId], (_name, _sessionId, signal, statusCallback) => {
40
+ // one status slot per assembly, so N concurrent reads aggregate into one
41
+ // bar instead of the last writer winning
42
+ const slot = createStatusFanOut(statusCallback);
43
+ // A `ReferenceSequenceTrack` config declares no `assemblyNames` slot: its
44
+ // assembly is the config node holding it, which `getConfAssemblyNames`
45
+ // walks to. It throws when a config has neither, and inside the fetcher
46
+ // that lands in the error banner rather than out of a render
47
+ return Promise.all([...new Set(getConfAssemblyNames(config))].map(async (assemblyName) => [
48
+ assemblyName,
49
+ await rpcManager.call(sessionId, 'CoreGetRefNames', {
50
+ adapterConfig,
51
+ assemblyName,
52
+ signal,
53
+ statusCallback: slot(),
54
+ }),
55
+ ]));
56
+ });
57
+ // undefined here means the key was incomplete and the fetch never ran, not
58
+ // that one is still in flight — `isLoading` is what says that. Treating the
59
+ // two as the same thing is what left the dialog spinning with nothing behind
60
+ // it; an empty list at least says so
61
+ const refNames = data ?? [];
62
+ return (_jsx(Dialog, { open: true, title: "Reference sequence names used in track", onClose: onClose, maxWidth: "xl", children: _jsx(DialogContent, { className: classes.container, children: error ? (_jsx(ErrorBanner, { error: error })) : isLoading ? (_jsx(LoadingEllipses, { message: statusProgressLabel(status) || 'Loading refNames' })) : (_jsxs(_Fragment, { children: [_jsx(CopyToClipboardButton, { variant: "contained", value: () => formatRefNames(refNames, false), children: "Copy ref names" }), _jsx("pre", { className: classes.refNames, children: formatRefNames(refNames, true) })] })) }) }));
81
63
  });
82
64
  export default RefNameInfoDialog;
@@ -0,0 +1,12 @@
1
+ export interface ViewPreferencesSession {
2
+ scrollZoom: boolean;
3
+ setScrollZoom: (flag: boolean) => void;
4
+ stickyViewHeaders: boolean;
5
+ setStickyViewHeaders: (sticky: boolean) => void;
6
+ effectiveUseWorkspaces: boolean;
7
+ setUseWorkspacesPreference: (useWorkspaces: boolean) => void;
8
+ }
9
+ declare const ViewPreferencesTab: ({ session, }: {
10
+ session: ViewPreferencesSession;
11
+ }) => import("react").JSX.Element;
12
+ export default ViewPreferencesTab;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SCROLL_ZOOM_HELP, SCROLL_ZOOM_LABEL, } from '@jbrowse/core/ui/scrollZoomLabels';
3
+ import { FormGroup } from '@mui/material';
4
+ import { observer } from 'mobx-react';
5
+ import PreferenceCheckbox from './PreferenceCheckbox.js';
6
+ const ViewPreferencesTab = observer(function ViewPreferencesTab({ session, }) {
7
+ return (_jsxs(FormGroup, { children: [_jsx(PreferenceCheckbox, { checked: session.scrollZoom, label: SCROLL_ZOOM_LABEL, help: SCROLL_ZOOM_HELP, onChange: checked => {
8
+ session.setScrollZoom(checked);
9
+ } }), _jsx(PreferenceCheckbox, { checked: session.stickyViewHeaders, label: "Keep view header visible", help: "The view header stays at the top of the page while its tracks scroll under it.", onChange: checked => {
10
+ session.setStickyViewHeaders(checked);
11
+ } }), _jsx(PreferenceCheckbox, { checked: session.effectiveUseWorkspaces, label: "Use workspaces", help: "Views open as tabs and tiles instead of stacking down the page.", onChange: checked => {
12
+ session.setUseWorkspacesPreference(checked);
13
+ } })] }));
14
+ });
15
+ export default ViewPreferencesTab;
@@ -0,0 +1,18 @@
1
+ import type { SessionWithWidgets, Widget } from '@jbrowse/core/util';
2
+ /**
3
+ * Renders one widget: resolve its React component, run it through the
4
+ * `Core-replaceWidget` fold, and contain both its lazy chunk and its errors.
5
+ *
6
+ * Only the chrome around a widget differs between surfaces — the drawer's
7
+ * resizable paper and header, the modal's dialog — so this is the part all of
8
+ * them share, spelled once. The error boundary in particular was previously
9
+ * only on the drawer path: popping the same widget out to a modal turned a
10
+ * widget that throws during render from a banner in the widget's own area into
11
+ * an unhandled throw that took the app down with it.
12
+ */
13
+ declare const WidgetBody: ({ session, widget, toolbarHeight, }: {
14
+ session: SessionWithWidgets;
15
+ widget: Widget;
16
+ toolbarHeight?: number;
17
+ }) => import("react").JSX.Element;
18
+ export default WidgetBody;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Suspense } from 'react';
3
+ import { ErrorBanner, LoadingEllipses, PluggableComponent, } from '@jbrowse/core/ui';
4
+ import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary';
5
+ import { getEnv } from '@jbrowse/core/util';
6
+ import { observer } from 'mobx-react';
7
+ /**
8
+ * Renders one widget: resolve its React component, run it through the
9
+ * `Core-replaceWidget` fold, and contain both its lazy chunk and its errors.
10
+ *
11
+ * Only the chrome around a widget differs between surfaces — the drawer's
12
+ * resizable paper and header, the modal's dialog — so this is the part all of
13
+ * them share, spelled once. The error boundary in particular was previously
14
+ * only on the drawer path: popping the same widget out to a modal turned a
15
+ * widget that throws during render from a banner in the widget's own area into
16
+ * an unhandled throw that took the app down with it.
17
+ */
18
+ const WidgetBody = observer(function WidgetBody({ session, widget, toolbarHeight = 0, }) {
19
+ const { pluginManager } = getEnv(session);
20
+ return (_jsx(Suspense, { fallback: _jsx(LoadingEllipses, {}), children: _jsx(ErrorBoundary, { FallbackComponent: ({ error }) => _jsx(ErrorBanner, { error: error }), children: _jsx(PluggableComponent, { pluginManager: pluginManager, name: "Core-replaceWidget", component: pluginManager.getWidgetType(widget.type).ReactComponent, props: { model: widget, session, toolbarHeight } }) }) }));
21
+ });
22
+ export default WidgetBody;
@@ -0,0 +1,7 @@
1
+ import type PluginManager from '@jbrowse/core/PluginManager';
2
+ import type { Widget } from '@jbrowse/core/util';
3
+ declare function WidgetHeading({ widget, pluginManager, }: {
4
+ widget: Widget;
5
+ pluginManager: PluginManager;
6
+ }): import("react").JSX.Element;
7
+ export default WidgetHeading;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Typography } from '@mui/material';
3
+ function WidgetHeading({ widget, pluginManager, }) {
4
+ const { HeadingComponent, heading } = pluginManager.getWidgetType(widget.type);
5
+ return HeadingComponent ? (_jsx(HeadingComponent, { model: widget })) : (_jsx(Typography, { variant: "h6", color: "inherit", children: heading }));
6
+ }
7
+ export default WidgetHeading;