@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
@@ -1,11 +1,20 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Attributes from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/Attributes';
3
+ import BaseCard from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/BaseCard';
4
+ import { hydrateTrackConfig } from '@jbrowse/core/configuration';
5
+ import { PluggableComponent } from '@jbrowse/core/ui';
2
6
  import Dialog from '@jbrowse/core/ui/Dialog';
3
7
  import { getEnv } from '@jbrowse/core/util';
4
8
  import { getTrackName } from '@jbrowse/core/util/tracks';
5
- import AboutContents from "./AboutDialogContents.js";
6
- export function AboutDialog({ config, session, handleClose, }) {
7
- const trackName = getTrackName(config, session);
9
+ import { isStateTreeNode } from '@jbrowse/mobx-state-tree';
10
+ import AboutContents from './AboutDialogContents.js';
11
+ export default function AboutDialog({ config, session, handleClose, }) {
8
12
  const { pluginManager } = getEnv(session);
9
- const AboutComponent = pluginManager.evaluateExtensionPoint('Core-replaceAbout', AboutContents, { session, config });
10
- return (_jsx(Dialog, { open: true, onClose: handleClose, title: trackName, maxWidth: "xl", children: _jsx(AboutComponent, { config: config, session: session }) }));
13
+ // hydrated once here, so every panel and every plugin on the two About points
14
+ // reads one live node: "Copy config" gives the same JSON whichever menu
15
+ // opened the dialog, and a jexl `formatAbout` slot has an env to evaluate in
16
+ const live = isStateTreeNode(config)
17
+ ? config
18
+ : hydrateTrackConfig(pluginManager, config);
19
+ return (_jsx(Dialog, { open: true, onClose: handleClose, title: getTrackName(config, session), maxWidth: "xl", children: live ? (_jsx(PluggableComponent, { pluginManager: pluginManager, name: "Core-replaceAbout", component: AboutContents, props: { config: live, session } })) : (_jsx(BaseCard, { title: "Configuration", children: _jsx(Attributes, { attributes: config }) })) }));
11
20
  }
@@ -1,7 +1,3 @@
1
- import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
2
- import type { AbstractSessionModel } from '@jbrowse/core/util';
3
- declare const AboutDialogContents: ({ config, session, }: {
4
- config: AnyConfigurationModel | Record<string, unknown>;
5
- session: AbstractSessionModel;
6
- }) => import("react/jsx-runtime").JSX.Element;
1
+ import type { AboutPanelProps } from './util.ts';
2
+ declare const AboutDialogContents: ({ config, session, }: AboutPanelProps) => import("react").JSX.Element;
7
3
  export default AboutDialogContents;
@@ -2,35 +2,31 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import Attributes from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/Attributes';
4
4
  import BaseCard from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/BaseCard';
5
- import { getConf } from '@jbrowse/core/configuration';
5
+ import { readConfObject } from '@jbrowse/core/configuration';
6
+ import PluggableComponents from '@jbrowse/core/ui/PluggableComponents';
6
7
  import { getEnv } from '@jbrowse/core/util';
7
8
  import { makeStyles } from '@jbrowse/core/util/tss-react';
8
- import { getSnapshot, isStateTreeNode } from '@jbrowse/mobx-state-tree';
9
9
  import { observer } from 'mobx-react';
10
- import FileInfoPanel from "./FileInfoPanel.js";
11
- import HeaderButtons from "./HeaderButtons.js";
12
- import RefNameInfoDialog from "./RefNameInfoDialog.js";
13
- import { generateDisplayableConfig, readConf } from "./util.js";
10
+ import AssemblyInfoPanel from './AssemblyInfoPanel.js';
11
+ import DescriptionPanel from './DescriptionPanel.js';
12
+ import FileInfoPanel from './FileInfoPanel.js';
13
+ import HeaderButtons from './HeaderButtons.js';
14
+ import RefNameInfoDialog from './RefNameInfoDialog.js';
15
+ import { getAboutDialogConfig } from './util.js';
14
16
  const useStyles = makeStyles()({
15
17
  content: {
16
18
  minWidth: 800,
17
19
  },
18
20
  });
21
+ const hideFields = ['displays', 'baseUri', 'refNames', 'formatAbout'];
19
22
  const AboutDialogContents = observer(function AboutDialogContents({ config, session, }) {
20
- const conf = isStateTreeNode(config) ? getSnapshot(config) : config;
21
23
  const { classes } = useStyles();
22
24
  const [showRefNames, setShowRefNames] = useState(false);
23
- const hideUris = getConf(session, ['formatAbout', 'hideUris']) ||
24
- readConf(config, ['formatAbout', 'hideUris']);
25
25
  const { pluginManager } = getEnv(session);
26
- const confPostExt = generateDisplayableConfig({
27
- config,
28
- session,
29
- pluginManager,
30
- });
31
- const ExtraPanel = pluginManager.evaluateExtensionPoint('Core-extraAboutPanel', null, { session, config });
32
- const hideFields = ['displays', 'baseUri', 'refNames', 'formatAbout'];
33
- return (_jsxs("div", { className: classes.content, children: [_jsxs(BaseCard, { title: "Configuration", children: [!hideUris ? (_jsx(HeaderButtons, { conf: conf, setShowRefNames: setShowRefNames })) : null, _jsx(Attributes, { attributes: confPostExt.config, omit: [...hideFields, 'metadata'], hideUris: hideUris })] }), confPostExt.config.metadata ? (_jsx(BaseCard, { title: "Metadata", children: _jsx(Attributes, { attributes: confPostExt.config.metadata, omit: hideFields, hideUris: hideUris }) })) : null, ExtraPanel ? (_jsx(BaseCard, { title: ExtraPanel.name, children: _jsx(ExtraPanel.Component, { config: config }) })) : null, _jsx(FileInfoPanel, { config: config, session: session }), showRefNames ? (_jsx(RefNameInfoDialog, { session: session, config: config, onClose: () => {
26
+ const { config: shown, hideUris } = getAboutDialogConfig({ config, session });
27
+ return (_jsxs("div", { className: classes.content, children: [_jsxs(BaseCard, { title: "Configuration", children: [_jsx(HeaderButtons, { conf: readConfObject(config), hideUris: hideUris, setShowRefNames: setShowRefNames }), _jsx(Attributes, { attributes: shown, omit: [...hideFields, 'metadata'], hideUris: hideUris })] }), shown.metadata ? (_jsx(BaseCard, { title: "Metadata", children: _jsx(Attributes, { attributes: shown.metadata, hideUris: hideUris }) })) : null, _jsx(DescriptionPanel, { config: config, session: session }), _jsx(AssemblyInfoPanel, { config: config, session: session, hideUris: hideUris }), _jsx(PluggableComponents, { pluginManager: pluginManager,
28
+ /** #extensionPoint Core-extraAboutPanel | sync | Add extra panels to a track's About dialog */
29
+ name: "Core-extraAboutPanel", props: { session, config } }), hideUris ? null : _jsx(FileInfoPanel, { config: config, session: session }), showRefNames ? (_jsx(RefNameInfoDialog, { session: session, config: config, onClose: () => {
34
30
  setShowRefNames(false);
35
31
  } })) : null] }));
36
32
  });
@@ -0,0 +1,5 @@
1
+ import type { AboutPanelProps } from './util.ts';
2
+ declare const AssemblyInfoPanel: ({ config, session, hideUris, }: AboutPanelProps & {
3
+ hideUris?: boolean;
4
+ }) => import("react").JSX.Element | null;
5
+ export default AssemblyInfoPanel;
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import Attributes from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/Attributes';
4
+ import BaseCard from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/BaseCard';
5
+ import { readConfObject } from '@jbrowse/core/configuration';
6
+ import { CopyToClipboardButton } from '@jbrowse/core/ui';
7
+ import { stripBaseUris } from '@jbrowse/core/util/addRelativeUris';
8
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
9
+ import { getParent, hasParent } from '@jbrowse/mobx-state-tree';
10
+ import { Button } from '@mui/material';
11
+ import { observer } from 'mobx-react';
12
+ import RefNameAliasesDialog from './RefNameAliasesDialog.js';
13
+ // `Attributes` omits these at every depth, which is what reaches the two that
14
+ // are nested:
15
+ // - `sequence` is the config of the very track this dialog is about, in the
16
+ // card above
17
+ // - `features` is an inline adapter's whole data payload. An assembly whose
18
+ // aliases or cytobands are written into the config rather than fetched from a
19
+ // file rendered them here as a spreadsheet of the raw rows, which buried the
20
+ // four lines anyone opened the card for — and it is the same data the alias
21
+ // listing shows resolved, one button over
22
+ // - `adapterId` is a generated hash, and `baseUri` is where the config was
23
+ // loaded from rather than anything about the assembly
24
+ //
25
+ // Nothing is hidden from "Copy assembly config", which copies the config whole.
26
+ const hideFields = ['sequence', 'features', 'adapterId', 'baseUri'];
27
+ const useStyles = makeStyles()(theme => ({
28
+ buttons: {
29
+ float: 'right',
30
+ display: 'flex',
31
+ gap: theme.spacing(1),
32
+ },
33
+ }));
34
+ /**
35
+ * The assembly config a `ReferenceSequenceTrack` config hangs off, and
36
+ * undefined for every other track.
37
+ *
38
+ * A reference sequence track declares no `assemblyNames` slot — the config node
39
+ * holding it *is* its assembly, the same walk `getConfAssemblyNames` makes.
40
+ * Compared by identity against that node's `sequence`, so the card is the
41
+ * assembly of the track the dialog is already showing.
42
+ */
43
+ function getAssemblyConf(config) {
44
+ const parent = hasParent(config)
45
+ ? getParent(config)
46
+ : undefined;
47
+ return parent?.sequence === config ? parent : undefined;
48
+ }
49
+ const AssemblyCard = observer(function AssemblyCard({ assemblyConf, session, hideUris, }) {
50
+ const { classes } = useStyles();
51
+ const [showAliases, setShowAliases] = useState(false);
52
+ const conf = readConfObject(assemblyConf);
53
+ return (_jsxs(BaseCard, { title: "Assembly", children: [_jsxs("span", { className: classes.buttons, children: [_jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
54
+ setShowAliases(true);
55
+ }, children: "Show ref name aliases" }), hideUris ? null : (_jsx(CopyToClipboardButton, { variant: "contained", value: () => JSON.stringify(stripBaseUris(structuredClone(conf)), null, 2), children: "Copy assembly config" }))] }), _jsx(Attributes, { attributes: conf, omit: hideFields, hideUris: hideUris }), showAliases ? (_jsx(RefNameAliasesDialog, { assemblyName: readConfObject(assemblyConf, 'name'), session: session, onClose: () => {
56
+ setShowAliases(false);
57
+ } })) : null] }));
58
+ });
59
+ const AssemblyInfoPanel = observer(function AssemblyInfoPanel({ config, session, hideUris, }) {
60
+ const assemblyConf = getAssemblyConf(config);
61
+ return assemblyConf ? (_jsx(AssemblyCard, { assemblyConf: assemblyConf, session: session, hideUris: hideUris })) : null;
62
+ });
63
+ export default AssemblyInfoPanel;
@@ -0,0 +1,3 @@
1
+ import type { AboutPanelProps } from './util.ts';
2
+ declare const DescriptionPanel: ({ config, }: AboutPanelProps) => import("react").JSX.Element | null;
3
+ export default DescriptionPanel;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import BaseCard from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/BaseCard';
3
+ import { readConfObject } from '@jbrowse/core/configuration';
4
+ import { ErrorBanner, LoadingEllipses, SanitizedHTML } from '@jbrowse/core/ui';
5
+ import { openLocation } from '@jbrowse/core/util/io';
6
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
7
+ import { useFetch } from '@jbrowse/core/util/useFetch';
8
+ import { Link, Typography } from '@mui/material';
9
+ import { observer } from 'mobx-react';
10
+ import { getDescriptionHtmlUrl, rebaseDescriptionHtml, } from './descriptionHtml.js';
11
+ const useStyles = makeStyles()(theme => ({
12
+ provenance: {
13
+ marginBottom: theme.spacing(1),
14
+ },
15
+ }));
16
+ const DescriptionCard = observer(function DescriptionCard({ url, }) {
17
+ const { classes } = useStyles();
18
+ const { data: html, error, isLoading, } = useFetch(['trackDescriptionHtml', url], async (_name, url) => rebaseDescriptionHtml(await openLocation({ uri: url, locationType: 'UriLocation' }).readFile('utf8'), url));
19
+ return (_jsxs(BaseCard, { title: "Description", children: [_jsxs(Typography, { variant: "caption", color: "textSecondary", component: "div", className: classes.provenance, children: ["From the track hub, written for the UCSC Genome Browser \u2014 some display options it describes differ in JBrowse.", ' ', _jsx(Link, { href: url, target: "_blank", rel: "noopener noreferrer", children: "View original" })] }), error ? (_jsx(ErrorBanner, { error: error })) : isLoading || html === undefined ? (_jsx(LoadingEllipses, { message: "Loading description" })) : (_jsx(SanitizedHTML, { html: html }))] }));
20
+ });
21
+ const DescriptionPanel = observer(function DescriptionPanel({ config, }) {
22
+ const url = getDescriptionHtmlUrl(readConfObject(config, 'metadata'));
23
+ return url ? _jsx(DescriptionCard, { url: url }) : null;
24
+ });
25
+ export default DescriptionPanel;
@@ -0,0 +1,21 @@
1
+ import type { DrawerPosition } from '@jbrowse/core/util';
2
+ /**
3
+ * All the chrome needs of a session, narrowed the way
4
+ * `PreferencesDialogSession` is: the drawer is the one widget surface an
5
+ * embedder can mount against a session of their own shape.
6
+ */
7
+ export interface DrawerChromeSession {
8
+ drawerPosition: DrawerPosition;
9
+ resizeDrawer: (distance: number, availableWidth?: number) => number;
10
+ }
11
+ /**
12
+ * The drawer's chrome: the paper it sits on and the handle that resizes it.
13
+ * Focus tracking is left to the caller through `ref`, since only the app shell
14
+ * has a focused-view concept to wire it to.
15
+ */
16
+ declare const Drawer: ({ session, children, ref, }: {
17
+ session: DrawerChromeSession;
18
+ children: React.ReactNode;
19
+ ref?: React.Ref<HTMLDivElement>;
20
+ }) => import("react").JSX.Element;
21
+ export default Drawer;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import ResizeHandle from '@jbrowse/core/ui/ResizeHandle';
4
+ import { DRAWER_Z_INDEX } from '@jbrowse/core/ui/zIndexes';
5
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
6
+ import { Paper } from '@mui/material';
7
+ import { observer } from 'mobx-react';
8
+ const useStyles = makeStyles()(theme => ({
9
+ // The grid item, and the handle's containing block. Its own box rather than
10
+ // the Paper's because the Paper is the thing that SCROLLS: a handle
11
+ // positioned against it starts at the top of the scrolled content and rides
12
+ // away with it, and one taken out of flow with `position: fixed` is measured
13
+ // against the window instead — which is the same box only in a full-window
14
+ // app, and put a page-tall col-resize strip down an embedded view's page.
15
+ root: {
16
+ // the drawer places itself, so a host renders it once wherever it likes
17
+ // rather than on the side matching `drawerPosition` (see
18
+ // `drawerGridTemplateColumns`)
19
+ gridColumn: 'drawer',
20
+ position: 'relative',
21
+ height: '100%',
22
+ minWidth: 0,
23
+ },
24
+ paper: {
25
+ overflowY: 'auto',
26
+ height: '100%',
27
+ // widgets position their own overlays against this
28
+ position: 'relative',
29
+ zIndex: DRAWER_Z_INDEX,
30
+ outline: 'none',
31
+ background: theme.palette.background.default,
32
+ },
33
+ resizeHandle: {
34
+ position: 'absolute',
35
+ top: 0,
36
+ zIndex: DRAWER_Z_INDEX + 1,
37
+ },
38
+ // whichever edge faces the main content
39
+ handleForLeftDrawer: { right: 0 },
40
+ handleForRightDrawer: { left: 0 },
41
+ }));
42
+ /**
43
+ * The drawer's chrome: the paper it sits on and the handle that resizes it.
44
+ * Focus tracking is left to the caller through `ref`, since only the app shell
45
+ * has a focused-view concept to wire it to.
46
+ */
47
+ const Drawer = observer(function Drawer({ session, children, ref, }) {
48
+ const { drawerPosition } = session;
49
+ const { classes, cx } = useStyles();
50
+ const rootRef = useRef(null);
51
+ return (_jsxs("div", { ref: rootRef, className: classes.root, children: [_jsx(Paper, { ref: ref, className: classes.paper, elevation: 16, square: true, "data-testid": "drawer-widget", children: children }), _jsx(ResizeHandle, { onDrag: distance => {
52
+ // the width the drawer and the main area share: both are columns of
53
+ // the grid this is an item of, and the clamp needs the container
54
+ // rather than the window
55
+ session.resizeDrawer(distance, rootRef.current?.parentElement?.clientWidth);
56
+ }, className: cx(classes.resizeHandle, drawerPosition === 'left'
57
+ ? classes.handleForLeftDrawer
58
+ : classes.handleForRightDrawer), vertical: true, bar: true })] }));
59
+ });
60
+ export default Drawer;
@@ -0,0 +1,5 @@
1
+ import type { SessionWithDrawerWidgets } from '@jbrowse/core/util';
2
+ declare const DrawerControls: ({ session, }: {
3
+ session: SessionWithDrawerWidgets;
4
+ }) => import("react").JSX.Element;
5
+ export default DrawerControls;
@@ -0,0 +1,28 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import CloseIcon from '@mui/icons-material/Close';
4
+ import MinimizeIcon from '@mui/icons-material/Minimize';
5
+ import MoreVertIcon from '@mui/icons-material/MoreVert';
6
+ import { IconButton, Menu, MenuItem, Tooltip } from '@mui/material';
7
+ import { observer } from 'mobx-react';
8
+ const drawerPositionOptions = ['left', 'right'];
9
+ const DrawerControls = observer(function DrawerControls({ session, }) {
10
+ const [anchorEl, setAnchorEl] = useState(null);
11
+ const { drawerPosition, visibleWidget } = session;
12
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { title: "Drawer position", children: _jsx(IconButton, { "data-testid": "drawer-position-button", color: "inherit", onClick: event => {
13
+ setAnchorEl(event.currentTarget);
14
+ }, children: _jsx(MoreVertIcon, {}) }) }), _jsx(Tooltip, { title: "Minimize drawer", children: _jsx(IconButton, { "data-testid": "drawer-minimize", color: "inherit", onClick: () => {
15
+ session.notify(`Drawer minimized, click button on ${drawerPosition} side of screen to re-open`, 'info');
16
+ session.minimizeWidgetDrawer();
17
+ }, children: _jsx(MinimizeIcon, {}) }) }), _jsx(Tooltip, { title: "Close drawer", children: _jsx(IconButton, { color: "inherit", onClick: () => {
18
+ if (visibleWidget) {
19
+ session.hideWidget(visibleWidget);
20
+ }
21
+ }, children: _jsx(CloseIcon, {}) }) }), _jsx(Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: () => {
22
+ setAnchorEl(null);
23
+ }, children: drawerPositionOptions.map(option => (_jsx(MenuItem, { selected: drawerPosition === option, onClick: () => {
24
+ session.setDrawerPosition(option);
25
+ setAnchorEl(null);
26
+ }, children: option }, option))) })] }));
27
+ });
28
+ export default DrawerControls;
@@ -0,0 +1,6 @@
1
+ import type { SessionWithDrawerWidgets } from '@jbrowse/core/util';
2
+ declare const DrawerHeader: ({ session, setToolbarHeight, }: {
3
+ session: SessionWithDrawerWidgets;
4
+ setToolbarHeight: (arg: number) => void;
5
+ }) => import("react").JSX.Element;
6
+ export default DrawerHeader;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback } from 'react';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import { AppBar, Toolbar } from '@mui/material';
5
+ import { observer } from 'mobx-react';
6
+ import DrawerControls from './DrawerControls.js';
7
+ import DrawerWidgetSelector from './DrawerWidgetSelector.js';
8
+ const useStyles = makeStyles()(theme => ({
9
+ appBar: {
10
+ background: theme.palette.secondary.main,
11
+ },
12
+ spacer: {
13
+ flexGrow: 1,
14
+ },
15
+ }));
16
+ const DrawerHeader = observer(function DrawerHeader({ session, setToolbarHeight, }) {
17
+ const { classes } = useStyles();
18
+ // Measured, and re-measured: the toolbar's height is the room a virtualized
19
+ // widget subtracts from the box it is given, and it changes without this
20
+ // component re-rendering -- MUI's Toolbar is 56px under the `sm` breakpoint
21
+ // and 64 above it, and grows again with the root font size. Read once at
22
+ // mount, a widget's list was that much too tall for the rest of the session.
23
+ const appBarRef = useCallback((node) => {
24
+ const publish = () => {
25
+ setToolbarHeight(node.getBoundingClientRect().height);
26
+ };
27
+ publish();
28
+ const observer = new ResizeObserver(publish);
29
+ observer.observe(node);
30
+ return () => {
31
+ observer.disconnect();
32
+ };
33
+ }, [setToolbarHeight]);
34
+ return (_jsx(AppBar, { position: "sticky", className: classes.appBar, ref: appBarRef, children: _jsxs(Toolbar, { disableGutters: true, children: [_jsx(DrawerWidgetSelector, { session: session }), _jsx("div", { className: classes.spacer }), _jsx(DrawerControls, { session: session })] }) }));
35
+ });
36
+ export default DrawerHeader;
@@ -0,0 +1,5 @@
1
+ import type { SessionWithDrawerWidgets } from '@jbrowse/core/util';
2
+ declare const DrawerWidget: ({ session, }: {
3
+ session: SessionWithDrawerWidgets;
4
+ }) => import("react").JSX.Element;
5
+ export default DrawerWidget;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { observer } from 'mobx-react';
4
+ import Drawer from './Drawer.js';
5
+ import DrawerHeader from './DrawerHeader.js';
6
+ import WidgetBody from './WidgetBody.js';
7
+ const DrawerWidget = observer(function DrawerWidget({ session, }) {
8
+ const { visibleWidget } = session;
9
+ const [toolbarHeight, setToolbarHeight] = useState(0);
10
+ return (_jsxs(Drawer, { session: session, children: [_jsx(DrawerHeader, { session: session, setToolbarHeight: setToolbarHeight }), visibleWidget ? (_jsx(WidgetBody, { session: session, widget: visibleWidget, toolbarHeight: toolbarHeight })) : null] }));
11
+ });
12
+ export default DrawerWidget;
@@ -0,0 +1,5 @@
1
+ import type { SessionWithDrawerWidgets } from '@jbrowse/core/util';
2
+ declare const DrawerWidgetSelector: ({ session, }: {
3
+ session: SessionWithDrawerWidgets;
4
+ }) => import("react").JSX.Element;
5
+ export default DrawerWidgetSelector;
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getEnv } from '@jbrowse/core/util';
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
4
+ import DeleteIcon from '@mui/icons-material/Delete';
5
+ import { FormControl, IconButton, MenuItem, Select, Typography, } from '@mui/material';
6
+ import { observer } from 'mobx-react';
7
+ import WidgetHeading from './WidgetHeading.js';
8
+ const useStyles = makeStyles()(theme => ({
9
+ formControl: {
10
+ margin: 0,
11
+ },
12
+ drawerSelect: {
13
+ margin: 0,
14
+ color: theme.palette.secondary.contrastText,
15
+ },
16
+ dropDownIcon: {
17
+ color: theme.palette.secondary.contrastText,
18
+ },
19
+ }));
20
+ const DrawerWidgetSelector = observer(function DrawerWidgetSelector({ session, }) {
21
+ const { visibleWidget, activeWidgets } = session;
22
+ const { classes } = useStyles();
23
+ const { pluginManager } = getEnv(session);
24
+ return (_jsx(FormControl, { className: classes.formControl, children: _jsx(Select, { value: visibleWidget?.id ?? '', "data-testid": "widget-drawer-selects", className: classes.drawerSelect, classes: { icon: classes.dropDownIcon }, renderValue: widgetId => {
25
+ const widget = session.activeWidgets.get(widgetId);
26
+ return widget ? (_jsx(WidgetHeading, { pluginManager: pluginManager, widget: widget })) : (_jsx(Typography, { variant: "h6", color: "inherit", children: "Unknown widget" }));
27
+ }, onChange: e => {
28
+ const w = session.activeWidgets.get(e.target.value);
29
+ if (w) {
30
+ session.showWidget(w);
31
+ }
32
+ else {
33
+ session.notify(`Widget not found ${e.target.value}`, 'warning');
34
+ }
35
+ }, children: [...activeWidgets.values()].map(widget => (_jsxs(MenuItem, { "data-testid": `widget-drawer-selects-item-${widget.type}`, value: widget.id, children: [_jsx(WidgetHeading, { pluginManager: pluginManager, widget: widget }), _jsx(IconButton, { "data-testid": `${widget.type}-drawer-delete`, color: "inherit", "aria-label": "Delete", onClick: e => {
36
+ e.stopPropagation();
37
+ session.hideWidget(widget);
38
+ }, children: _jsx(DeleteIcon, {}) })] }, widget.id))) }) }));
39
+ });
40
+ export default DrawerWidgetSelector;
@@ -1,6 +1,3 @@
1
- import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
2
- import type { AbstractSessionModel } from '@jbrowse/core/util';
3
- export default function FileInfoPanel({ config, session, }: {
4
- config: AnyConfigurationModel | Record<string, unknown>;
5
- session: AbstractSessionModel;
6
- }): import("react/jsx-runtime").JSX.Element;
1
+ import type { AboutPanelProps } from './util.ts';
2
+ declare const FileInfoPanel: ({ config, session, }: AboutPanelProps) => import("react").JSX.Element | null;
3
+ export default FileInfoPanel;
@@ -1,36 +1,46 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useState } from 'react';
3
2
  import Attributes from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/Attributes';
4
3
  import BaseCard from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/BaseCard';
5
- import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
6
- import { readConf } from "./util.js";
7
- export default function FileInfoPanel({ config, session, }) {
8
- const [error, setError] = useState();
9
- const [info, setInfo] = useState();
4
+ import { readConfObject } from '@jbrowse/core/configuration';
5
+ import { ErrorBanner, LoadingEllipses } from '@jbrowse/core/ui';
6
+ import { statusProgressLabel } from '@jbrowse/core/util';
7
+ import { getConfAssemblyNamesOrNone, getConfRpcSessionId, } from '@jbrowse/core/util/tracks';
8
+ import { useFetch } from '@jbrowse/core/util/useFetch';
9
+ import { observer } from 'mobx-react';
10
+ const FileInfoPanel = observer(function FileInfoPanel({ config, session, }) {
10
11
  const { rpcManager } = session;
11
- const trackId = readConf(config, 'trackId');
12
- useEffect(() => {
13
- ;
14
- (async () => {
15
- try {
16
- const adapterConfig = readConf(config, 'adapter');
17
- const result = await rpcManager.call(trackId, 'CoreGetInfo', {
18
- adapterConfig,
19
- });
20
- setInfo(result);
21
- }
22
- catch (e) {
23
- console.error(e);
24
- setError(e);
25
- }
26
- })();
27
- }, [config, rpcManager, trackId]);
12
+ const adapterConfig = readConfObject(config, 'adapter');
13
+ const sessionId = getConfRpcSessionId(config);
14
+ // named so a reference-reading adapter resolves to the instance the track's
15
+ // own requests built, not a spare one
16
+ const [assemblyName] = getConfAssemblyNamesOrNone(config);
17
+ const { data: info, error, isLoading, status, } = useFetch(['CoreGetInfo', sessionId],
18
+ // reading a header can mean walking a v8 .hic's norm-vector index or a
19
+ // multi-megabyte VCF header, so it gets both handles: closing the About
20
+ // dialog stops it, and the wait says what it is
21
+ async (_name, _sessionId, signal, statusCallback) => (await rpcManager.call(sessionId, 'CoreGetInfo', {
22
+ adapterConfig,
23
+ assemblyName,
24
+ signal,
25
+ statusCallback,
26
+ })));
28
27
  const details = typeof info === 'string'
29
28
  ? {
30
29
  header: `<pre>${info
30
+ .replaceAll('&', '&amp;')
31
31
  .replaceAll('<', '&lt;')
32
32
  .replaceAll('>', '&gt;')}</pre>`,
33
33
  }
34
- : info || {};
35
- return (_jsx(BaseCard, { title: "File info", children: error ? (_jsx(ErrorMessage, { error: error })) : info === undefined ? (_jsx(LoadingEllipses, { message: "Loading file data" })) : (_jsx(Attributes, { attributes: details })) }));
36
- }
34
+ : (info ?? {});
35
+ // `BaseFeatureDataAdapter.getHeader` returns null unless an adapter overrides
36
+ // it, and `CoreGetInfo` returns null outright for anything that isn't a
37
+ // feature adapter — so most tracks reached this with nothing to say and got a
38
+ // FILE INFO heading with blank space under it. A panel with no content is not
39
+ // a panel
40
+ const empty = Object.values(details).every(v => v == null);
41
+ if (!error && !isLoading && empty) {
42
+ return null;
43
+ }
44
+ return (_jsx(BaseCard, { title: "File info", children: error ? (_jsx(ErrorBanner, { error: error })) : isLoading ? (_jsx(LoadingEllipses, { message: statusProgressLabel(status) || 'Loading file data' })) : (_jsx(Attributes, { attributes: details })) }));
45
+ });
46
+ export default FileInfoPanel;
@@ -0,0 +1,17 @@
1
+ import type { ThemeModeSelection } from '../Session/Themes.ts';
2
+ import type { ThemeMap } from '@jbrowse/core/ui';
3
+ import type { AnimationMode } from '@jbrowse/core/util';
4
+ export interface GeneralPreferencesSession {
5
+ allThemes: () => ThemeMap;
6
+ themeName?: string;
7
+ setThemeName: (arg: string) => void;
8
+ themeMode: ThemeModeSelection;
9
+ setThemeMode: (arg: ThemeModeSelection) => void;
10
+ animationMode: AnimationMode;
11
+ numberGrouping: boolean;
12
+ setPreferenceOverride: (key: string, value: unknown) => void;
13
+ }
14
+ declare const GeneralPreferencesTab: ({ session, }: {
15
+ session: GeneralPreferencesSession;
16
+ }) => import("react").JSX.Element;
17
+ export default GeneralPreferencesTab;
@@ -0,0 +1,34 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
3
+ import { FormGroup, MenuItem, TextField } from '@mui/material';
4
+ import { observer } from 'mobx-react';
5
+ import PreferenceCheckbox from './PreferenceCheckbox.js';
6
+ const useStyles = makeStyles()({
7
+ field: {
8
+ marginTop: 16,
9
+ display: 'block',
10
+ },
11
+ });
12
+ const THEME_MODES = [
13
+ { value: 'light', label: 'Light' },
14
+ { value: 'dark', label: 'Dark' },
15
+ { value: 'system', label: 'Follow system' },
16
+ ];
17
+ const ANIMATION_MODES = [
18
+ { value: 'system', label: 'Follow system (reduced motion)' },
19
+ { value: 'enabled', label: 'Always on' },
20
+ { value: 'disabled', label: 'Off' },
21
+ ];
22
+ const GeneralPreferencesTab = observer(function GeneralPreferencesTab({ session, }) {
23
+ const { classes } = useStyles();
24
+ return (_jsxs(_Fragment, { children: [_jsx(TextField, { select: true, variant: "outlined", className: classes.field, label: "Theme", value: session.themeName, onChange: event => {
25
+ session.setThemeName(event.target.value);
26
+ }, children: Object.entries(session.allThemes()).map(([key, val]) => (_jsx(MenuItem, { value: key, children: val.name || '(Unknown name)' }, key))) }), _jsx(TextField, { select: true, variant: "outlined", className: classes.field, label: "Light/dark", value: session.themeMode, onChange: event => {
27
+ session.setThemeMode(event.target.value);
28
+ }, children: THEME_MODES.map(opt => (_jsx(MenuItem, { value: opt.value, children: opt.label }, opt.value))) }), _jsx(TextField, { select: true, variant: "outlined", className: classes.field, label: "Animations", value: session.animationMode, onChange: event => {
29
+ session.setPreferenceOverride('animationMode', event.target.value);
30
+ }, children: ANIMATION_MODES.map(opt => (_jsx(MenuItem, { value: opt.value, children: opt.label }, opt.value))) }), _jsx(FormGroup, { children: _jsx(PreferenceCheckbox, { checked: session.numberGrouping, label: "Thousand separators in numbers", help: "Shows chr1:1,234,567 rather than chr1:1234567. Takes effect after reloading the page.", onChange: checked => {
31
+ session.setPreferenceOverride('numberGrouping', checked);
32
+ } }) })] }));
33
+ });
34
+ export default GeneralPreferencesTab;
@@ -1,6 +1,7 @@
1
1
  interface HeaderButtonsProps {
2
2
  conf: Record<string, unknown>;
3
+ hideUris?: boolean;
3
4
  setShowRefNames: (show: boolean) => void;
4
5
  }
5
- declare function HeaderButtons({ conf, setShowRefNames }: HeaderButtonsProps): import("react/jsx-runtime").JSX.Element;
6
+ declare function HeaderButtons({ conf, hideUris, setShowRefNames, }: HeaderButtonsProps): import("react").JSX.Element;
6
7
  export default HeaderButtons;
@@ -1,26 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { CopyToClipboardButton } from '@jbrowse/core/ui';
3
+ import { stripBaseUris } from '@jbrowse/core/util/addRelativeUris';
3
4
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
5
  import { Button } from '@mui/material';
5
- import { removeAttr } from "./util.js";
6
- const useStyles = makeStyles()({
6
+ const useStyles = makeStyles()(theme => ({
7
7
  button: {
8
8
  float: 'right',
9
9
  },
10
- });
11
- function HeaderButtons({ conf, setShowRefNames }) {
12
- const [copied, setCopied] = useState(false);
10
+ // JSX drops the whitespace between two elements on separate lines, so the two
11
+ // contained buttons rendered edge to edge, reading as one two-tone control
12
+ buttons: {
13
+ display: 'flex',
14
+ gap: theme.spacing(1),
15
+ justifyContent: 'flex-end',
16
+ },
17
+ }));
18
+ function HeaderButtons({ conf, hideUris, setShowRefNames, }) {
13
19
  const { classes } = useStyles();
14
- return (_jsxs("span", { className: classes.button, children: [_jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
15
- setShowRefNames(true);
16
- }, children: "Show ref names" }), _jsx(Button, { variant: "contained", onClick: async () => {
17
- const { default: copy } = await import('copy-to-clipboard');
18
- const snap = removeAttr(structuredClone(conf), 'baseUri');
19
- copy(JSON.stringify(snap, null, 2));
20
- setCopied(true);
21
- setTimeout(() => {
22
- setCopied(false);
23
- }, 1000);
24
- }, children: copied ? 'Copied to clipboard!' : 'Copy config' })] }));
20
+ return (_jsx("span", { className: classes.button, children: _jsxs("span", { className: classes.buttons, children: [_jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
21
+ setShowRefNames(true);
22
+ }, children: "Show ref names" }), hideUris ? null : (_jsx(CopyToClipboardButton, { variant: "contained", value: () => JSON.stringify(stripBaseUris(structuredClone(conf)), null, 2), children: "Copy config" }))] }) }));
25
23
  }
26
24
  export default HeaderButtons;