@jbrowse/product-core 4.3.0 → 5.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/RootModel/BaseRootModel.d.ts +629 -304
- package/esm/RootModel/BaseRootModel.js +139 -14
- package/esm/RootModel/HierarchicalConfig.d.ts +57 -21
- package/esm/RootModel/HierarchicalConfig.js +36 -0
- package/esm/RootModel/InternetAccounts.d.ts +18 -1
- package/esm/RootModel/InternetAccounts.js +120 -22
- package/esm/RootModel/PreferencesConfig.d.ts +68 -0
- package/esm/RootModel/PreferencesConfig.js +76 -0
- package/esm/RootModel/createConfigModel.d.ts +240 -0
- package/esm/RootModel/createConfigModel.js +56 -0
- package/esm/RootModel/index.d.ts +10 -5
- package/esm/RootModel/index.js +10 -5
- package/esm/RootModel/menuItems.d.ts +24 -0
- package/esm/RootModel/menuItems.js +143 -0
- package/esm/Session/BaseSession.d.ts +312 -11
- package/esm/Session/BaseSession.js +458 -14
- package/esm/Session/Connections.d.ts +114 -8
- package/esm/Session/Connections.js +195 -74
- package/esm/Session/DrawerWidgets.d.ts +115 -12
- package/esm/Session/DrawerWidgets.js +161 -42
- package/esm/Session/MultipleViews.d.ts +1281 -1128
- package/esm/Session/MultipleViews.js +396 -64
- package/esm/Session/Preferences.d.ts +1667 -0
- package/esm/Session/Preferences.js +59 -0
- package/esm/Session/ReferenceManagement.d.ts +28 -3
- package/esm/Session/ReferenceManagement.js +56 -41
- package/esm/Session/SessionTracks.d.ts +1277 -1138
- package/esm/Session/SessionTracks.js +540 -35
- package/esm/Session/Themes.d.ts +87 -2
- package/esm/Session/Themes.js +156 -23
- package/esm/Session/TrackMenu.d.ts +112 -0
- package/esm/Session/TrackMenu.js +200 -0
- package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
- package/esm/Session/TrackMenuSessionMixin.js +41 -0
- package/esm/Session/Tracks.d.ts +1171 -1117
- package/esm/Session/Tracks.js +183 -20
- package/esm/Session/assertSessionModel.d.ts +21 -0
- package/esm/Session/assertSessionModel.js +1 -0
- package/esm/Session/developerMode.d.ts +7 -0
- package/esm/Session/developerMode.js +43 -0
- package/esm/Session/finalizeSession.d.ts +16 -0
- package/esm/Session/finalizeSession.js +29 -0
- package/esm/Session/index.d.ts +22 -10
- package/esm/Session/index.js +13 -10
- package/esm/Session/readdedTrackConf.d.ts +8 -0
- package/esm/Session/readdedTrackConf.js +25 -0
- package/esm/Session/shareableSnapshot.d.ts +77 -0
- package/esm/Session/shareableSnapshot.js +196 -0
- package/esm/Session/temporaryAssemblyTracks.d.ts +24 -0
- package/esm/Session/temporaryAssemblyTracks.js +37 -0
- package/esm/assertExtends.d.ts +55 -0
- package/esm/assertExtends.js +1 -0
- package/esm/controllerTracks.d.ts +56 -0
- package/esm/controllerTracks.js +90 -0
- package/esm/destroyViewState.d.ts +21 -0
- package/esm/destroyViewState.js +29 -0
- package/esm/index.d.ts +31 -4
- package/esm/index.js +31 -4
- package/esm/loadPlugins.d.ts +33 -0
- package/esm/loadPlugins.js +41 -0
- package/esm/localFiles.d.ts +74 -0
- package/esm/localFiles.js +156 -0
- package/esm/observeSession.d.ts +56 -0
- package/esm/observeSession.js +91 -0
- package/esm/pluginInput.d.ts +32 -0
- package/esm/pluginInput.js +19 -0
- package/esm/pruneUnbuildableNodes.d.ts +109 -0
- package/esm/pruneUnbuildableNodes.js +568 -0
- package/esm/resolveAssemblies.d.ts +44 -0
- package/esm/resolveAssemblies.js +55 -0
- package/esm/rpcWorker.d.ts +26 -0
- package/esm/rpcWorker.js +98 -16
- package/esm/scheduleDetachedDestroy.d.ts +18 -0
- package/esm/scheduleDetachedDestroy.js +24 -0
- package/esm/sessionMigrations/index.d.ts +53 -0
- package/esm/sessionMigrations/index.js +432 -0
- package/esm/sessionUrl.d.ts +41 -0
- package/esm/sessionUrl.js +53 -0
- package/esm/sessionUtils.d.ts +69 -0
- package/esm/sessionUtils.js +613 -0
- package/esm/siblingCast.d.ts +14 -0
- package/esm/siblingCast.js +16 -0
- package/esm/snapshotUtils.d.ts +8 -0
- package/esm/snapshotUtils.js +12 -0
- package/esm/ui/AboutDialog.d.ts +3 -6
- package/esm/ui/AboutDialog.js +4 -4
- package/esm/ui/AboutDialogContents.d.ts +2 -6
- package/esm/ui/AboutDialogContents.js +34 -13
- package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
- package/esm/ui/AssemblyInfoPanel.js +69 -0
- package/esm/ui/DisplayDefaultsSection.d.ts +36 -0
- package/esm/ui/DisplayDefaultsSection.js +65 -0
- package/esm/ui/Drawer.d.ts +21 -0
- package/esm/ui/Drawer.js +60 -0
- package/esm/ui/DrawerControls.d.ts +5 -0
- package/esm/ui/DrawerControls.js +28 -0
- package/esm/ui/DrawerHeader.d.ts +6 -0
- package/esm/ui/DrawerHeader.js +36 -0
- package/esm/ui/DrawerWidget.d.ts +5 -0
- package/esm/ui/DrawerWidget.js +12 -0
- package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
- package/esm/ui/DrawerWidgetSelector.js +40 -0
- package/esm/ui/FileInfoPanel.d.ts +3 -6
- package/esm/ui/FileInfoPanel.js +35 -26
- package/esm/ui/HeaderButtons.d.ts +7 -1
- package/esm/ui/HeaderButtons.js +23 -18
- package/esm/ui/ModalWidget.d.ts +12 -0
- package/esm/ui/ModalWidget.js +33 -0
- package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
- package/esm/ui/ModalWidgetAppBar.js +9 -0
- package/esm/ui/PreferencesDialog.d.ts +56 -0
- package/esm/ui/PreferencesDialog.js +145 -0
- package/esm/ui/PreferencesResetDialog.d.ts +8 -0
- package/esm/ui/PreferencesResetDialog.js +19 -0
- package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
- package/esm/ui/RefNameAliasesDialog.js +65 -0
- package/esm/ui/RefNameInfoDialog.d.ts +3 -6
- package/esm/ui/RefNameInfoDialog.js +57 -63
- package/esm/ui/WidgetBody.d.ts +18 -0
- package/esm/ui/WidgetBody.js +22 -0
- package/esm/ui/WidgetHeading.d.ts +7 -0
- package/esm/ui/WidgetHeading.js +7 -0
- package/esm/ui/drawerLayout.d.ts +18 -0
- package/esm/ui/drawerLayout.js +22 -0
- package/esm/ui/index.d.ts +13 -1
- package/esm/ui/index.js +12 -1
- package/esm/ui/util.d.ts +37 -4
- package/esm/ui/util.js +35 -38
- package/esm/useEngineLifecycle.d.ts +19 -0
- package/esm/useEngineLifecycle.js +44 -0
- package/package.json +12 -15
- package/esm/RootModel/FormatAbout.d.ts +0 -12
- package/esm/RootModel/FormatAbout.js +0 -15
- package/esm/RootModel/FormatDetails.d.ts +0 -24
- package/esm/RootModel/FormatDetails.js +0 -27
- package/esm/Session/DialogQueue.d.ts +0 -17
- package/esm/Session/DialogQueue.js +0 -31
package/esm/Session/Themes.d.ts
CHANGED
|
@@ -1,18 +1,103 @@
|
|
|
1
1
|
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { SerializableThemeArgs, ThemeMap } from '@jbrowse/core/ui';
|
|
2
3
|
import type { IAnyStateTreeNode, Instance } from '@jbrowse/mobx-state-tree';
|
|
3
4
|
import type { ThemeOptions } from '@mui/material';
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* #stateModel ThemeManagerSessionMixin
|
|
7
|
+
*/
|
|
5
8
|
export declare function ThemeManagerSessionMixin(_pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{}, {
|
|
6
9
|
sessionThemeName: string;
|
|
7
10
|
} & {
|
|
11
|
+
/**
|
|
12
|
+
* #method
|
|
13
|
+
*/
|
|
8
14
|
allThemes(): ThemeMap;
|
|
15
|
+
/**
|
|
16
|
+
* #getter
|
|
17
|
+
*/
|
|
9
18
|
readonly themeName: string;
|
|
19
|
+
/**
|
|
20
|
+
* #getter
|
|
21
|
+
*/
|
|
22
|
+
readonly themeOptions: SerializableThemeArgs;
|
|
23
|
+
/**
|
|
24
|
+
* #getter
|
|
25
|
+
* Every color JBrowse renders, resolved to plain strings. This is what
|
|
26
|
+
* rendering reads: it needs no React context, it crosses the RPC worker
|
|
27
|
+
* boundary as itself, and it costs no UI toolkit. Prefer it over
|
|
28
|
+
* `theme` anywhere the answer wanted is a color rather than a Material
|
|
29
|
+
* UI component style.
|
|
30
|
+
*/
|
|
31
|
+
readonly palette: import("@jbrowse/core/ui/palette").JBrowsePalette;
|
|
32
|
+
/**
|
|
33
|
+
* #getter
|
|
34
|
+
* The palette plus the sizing tokens `makeStyles` reads — spacing,
|
|
35
|
+
* corner radius, type scale. This is what a product mounts on
|
|
36
|
+
* `StyleThemeProvider`; it costs no UI toolkit, and it is derived from
|
|
37
|
+
* the same `themeOptions` as `theme`, so a config `theme` that sets
|
|
38
|
+
* `spacing` moves JBrowse's own styles and its Material components
|
|
39
|
+
* together.
|
|
40
|
+
*/
|
|
41
|
+
readonly styleTheme: import("@jbrowse/core/ui/styleTheme").JBrowseStyleTheme;
|
|
42
|
+
/**
|
|
43
|
+
* #getter
|
|
44
|
+
* The Material UI theme, for the components that are Material UI. Its
|
|
45
|
+
* palette is spliced from the same `resolvePalette` call as `palette`
|
|
46
|
+
* above, so the two cannot disagree.
|
|
47
|
+
*/
|
|
10
48
|
readonly theme: import("@mui/material").Theme;
|
|
49
|
+
/**
|
|
50
|
+
* #method
|
|
51
|
+
* Raw `ThemeOptions` for the active theme, or a named override (used by
|
|
52
|
+
* the SVG-export theme picker). Unlike `theme` (a built,
|
|
53
|
+
* non-serializable MUI theme), this is the plain options object every
|
|
54
|
+
* view's SVG export threads into each display's `renderSvg`, which
|
|
55
|
+
* rebuilds the theme via `createJBrowseTheme` outside React context.
|
|
56
|
+
*
|
|
57
|
+
* The `default` entry is spliced with the config `theme` slot, because
|
|
58
|
+
* the preset is only half of what that entry means — the picker calls it
|
|
59
|
+
* "Default (from config)" and `resolvePalette` merges the two for every
|
|
60
|
+
* other consumer. Returning the bare preset made `view.exportSvg()`
|
|
61
|
+
* silently drop a host's configured palette: a config setting
|
|
62
|
+
* `primary.main` drew `#123456` on screen and exported the stock
|
|
63
|
+
* `#0D233F`, with the export dialog reporting the theme it had not used.
|
|
64
|
+
* Every other named theme is a fixed preset that ignores config, which is
|
|
65
|
+
* the distinction this ternary keeps.
|
|
66
|
+
*/
|
|
67
|
+
getActiveThemeOptions(name?: string): (ThemeOptions & {
|
|
68
|
+
name?: string;
|
|
69
|
+
}) | undefined;
|
|
11
70
|
} & {
|
|
71
|
+
/**
|
|
72
|
+
* #action
|
|
73
|
+
*/
|
|
12
74
|
setThemeName(name: string): void;
|
|
75
|
+
/**
|
|
76
|
+
* #action
|
|
77
|
+
* Point the session at light or dark, for a host that follows its own
|
|
78
|
+
* dark-mode state rather than offering JBrowse's theme menu. Satisfies
|
|
79
|
+
* `ThemeModeSession`, so `useSessionPalette` works against an app
|
|
80
|
+
* session and an embedded one alike.
|
|
81
|
+
*
|
|
82
|
+
* Expressed as a write to the config `theme` slot plus a return to the
|
|
83
|
+
* `default` theme, not as `setThemeName('darkStock')`. Only the
|
|
84
|
+
* `default` theme merges `configTheme.palette` (see `resolvePalette`),
|
|
85
|
+
* so selecting a stock theme would discard whatever the host passed as
|
|
86
|
+
* `configuration.theme` — their brand `primary`, say — the first time
|
|
87
|
+
* their toggle fired. Merging at both levels for the same reason:
|
|
88
|
+
* `theme` is a frozen slot, and `mode` and `primary` are siblings under
|
|
89
|
+
* `palette`.
|
|
90
|
+
*
|
|
91
|
+
* One write, not two: `themeOptions` is derived from the same slot and
|
|
92
|
+
* is what ships to the RPC worker, so the labels baked into a rendered
|
|
93
|
+
* image follow the mode along with what React draws.
|
|
94
|
+
*/
|
|
95
|
+
setThemeMode(mode: 'light' | 'dark'): void;
|
|
13
96
|
afterAttach(): void;
|
|
14
97
|
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|
|
98
|
+
/** Session mixin MST type for a session that supports theming */
|
|
15
99
|
export type SessionWithThemesType = ReturnType<typeof ThemeManagerSessionMixin>;
|
|
100
|
+
/** Instance of a session that has theming support */
|
|
16
101
|
export type SessionWithThemes = Instance<SessionWithThemesType>;
|
|
102
|
+
/** Type guard for SessionWithThemes */
|
|
17
103
|
export declare function isSessionWithThemes(session: IAnyStateTreeNode): session is SessionWithThemes;
|
|
18
|
-
export {};
|
package/esm/Session/Themes.js
CHANGED
|
@@ -1,43 +1,176 @@
|
|
|
1
|
-
import { getConf } from '@jbrowse/core/configuration';
|
|
2
|
-
import {
|
|
1
|
+
import { getConf, setConf } from '@jbrowse/core/configuration';
|
|
2
|
+
import { createJBrowseThemeFromArgs, defaultThemes } from '@jbrowse/core/ui';
|
|
3
|
+
import { resolvePalette } from '@jbrowse/core/ui/palette';
|
|
4
|
+
import { resolveStyleTheme } from '@jbrowse/core/ui/styleTheme';
|
|
3
5
|
import { localStorageGetItem, localStorageSetItem } from '@jbrowse/core/util';
|
|
4
6
|
import { addDisposer, types } from '@jbrowse/mobx-state-tree';
|
|
5
7
|
import { autorun } from 'mobx';
|
|
8
|
+
import { asSession } from '../siblingCast.js';
|
|
9
|
+
import { isBaseSession } from './BaseSession.js';
|
|
10
|
+
/**
|
|
11
|
+
* What `name` actually resolves to — `default` for one `themes` no longer
|
|
12
|
+
* holds. Every theme name is either stored or handed in from outside, and both
|
|
13
|
+
* go stale the same way: `sessionThemeName` outlives an `extraThemes` entry an
|
|
14
|
+
* admin drops, and so does a name an export dialog persisted or a saved figure
|
|
15
|
+
* spec carries.
|
|
16
|
+
*/
|
|
17
|
+
function resolveThemeName(themes, name) {
|
|
18
|
+
return themes[name] ? name : 'default';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* #stateModel ThemeManagerSessionMixin
|
|
22
|
+
*/
|
|
6
23
|
export function ThemeManagerSessionMixin(_pluginManager) {
|
|
7
24
|
return types
|
|
8
25
|
.model({})
|
|
9
26
|
.volatile(() => ({
|
|
10
|
-
sessionThemeName: localStorageGetItem('themeName')
|
|
11
|
-
}))
|
|
12
|
-
.views(s => ({
|
|
13
|
-
allThemes() {
|
|
14
|
-
const self = s;
|
|
15
|
-
const extraThemes = getConf(self.jbrowse, 'extraThemes');
|
|
16
|
-
return { ...defaultThemes, ...extraThemes };
|
|
17
|
-
},
|
|
18
|
-
get themeName() {
|
|
19
|
-
const { sessionThemeName } = s;
|
|
20
|
-
const all = this.allThemes();
|
|
21
|
-
return all[sessionThemeName] ? sessionThemeName : 'default';
|
|
22
|
-
},
|
|
23
|
-
get theme() {
|
|
24
|
-
const self = s;
|
|
25
|
-
const configTheme = getConf(self.jbrowse, 'theme');
|
|
26
|
-
const all = this.allThemes();
|
|
27
|
-
return createJBrowseTheme(configTheme, all, this.themeName);
|
|
28
|
-
},
|
|
27
|
+
sessionThemeName: localStorageGetItem('themeName') ?? 'default',
|
|
29
28
|
}))
|
|
29
|
+
.views(s => {
|
|
30
|
+
const self = asSession(s);
|
|
31
|
+
return {
|
|
32
|
+
/**
|
|
33
|
+
* #method
|
|
34
|
+
*/
|
|
35
|
+
allThemes() {
|
|
36
|
+
const extraThemes = getConf(self.jbrowse, 'extraThemes');
|
|
37
|
+
return { ...defaultThemes, ...extraThemes };
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* #getter
|
|
41
|
+
*/
|
|
42
|
+
get themeName() {
|
|
43
|
+
return resolveThemeName(this.allThemes(), self.sessionThemeName);
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* #getter
|
|
47
|
+
*/
|
|
48
|
+
// Structurally-serializable description of the active theme, safe to
|
|
49
|
+
// send across the RPC worker boundary (the created `theme` carries
|
|
50
|
+
// functions and cannot be cloned). The worker rebuilds via
|
|
51
|
+
// createJBrowseThemeFromArgs.
|
|
52
|
+
get themeOptions() {
|
|
53
|
+
return {
|
|
54
|
+
configTheme: getConf(self.jbrowse, 'theme'),
|
|
55
|
+
extraThemes: getConf(self.jbrowse, 'extraThemes'),
|
|
56
|
+
themeName: this.themeName,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* #getter
|
|
61
|
+
* Every color JBrowse renders, resolved to plain strings. This is what
|
|
62
|
+
* rendering reads: it needs no React context, it crosses the RPC worker
|
|
63
|
+
* boundary as itself, and it costs no UI toolkit. Prefer it over
|
|
64
|
+
* `theme` anywhere the answer wanted is a color rather than a Material
|
|
65
|
+
* UI component style.
|
|
66
|
+
*/
|
|
67
|
+
get palette() {
|
|
68
|
+
return resolvePalette(this.themeOptions);
|
|
69
|
+
},
|
|
70
|
+
/**
|
|
71
|
+
* #getter
|
|
72
|
+
* The palette plus the sizing tokens `makeStyles` reads — spacing,
|
|
73
|
+
* corner radius, type scale. This is what a product mounts on
|
|
74
|
+
* `StyleThemeProvider`; it costs no UI toolkit, and it is derived from
|
|
75
|
+
* the same `themeOptions` as `theme`, so a config `theme` that sets
|
|
76
|
+
* `spacing` moves JBrowse's own styles and its Material components
|
|
77
|
+
* together.
|
|
78
|
+
*/
|
|
79
|
+
get styleTheme() {
|
|
80
|
+
return resolveStyleTheme(this.themeOptions);
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* #getter
|
|
84
|
+
* The Material UI theme, for the components that are Material UI. Its
|
|
85
|
+
* palette is spliced from the same `resolvePalette` call as `palette`
|
|
86
|
+
* above, so the two cannot disagree.
|
|
87
|
+
*/
|
|
88
|
+
get theme() {
|
|
89
|
+
return createJBrowseThemeFromArgs(this.themeOptions);
|
|
90
|
+
},
|
|
91
|
+
/**
|
|
92
|
+
* #method
|
|
93
|
+
* Raw `ThemeOptions` for the active theme, or a named override (used by
|
|
94
|
+
* the SVG-export theme picker). Unlike `theme` (a built,
|
|
95
|
+
* non-serializable MUI theme), this is the plain options object every
|
|
96
|
+
* view's SVG export threads into each display's `renderSvg`, which
|
|
97
|
+
* rebuilds the theme via `createJBrowseTheme` outside React context.
|
|
98
|
+
*
|
|
99
|
+
* The `default` entry is spliced with the config `theme` slot, because
|
|
100
|
+
* the preset is only half of what that entry means — the picker calls it
|
|
101
|
+
* "Default (from config)" and `resolvePalette` merges the two for every
|
|
102
|
+
* other consumer. Returning the bare preset made `view.exportSvg()`
|
|
103
|
+
* silently drop a host's configured palette: a config setting
|
|
104
|
+
* `primary.main` drew `#123456` on screen and exported the stock
|
|
105
|
+
* `#0D233F`, with the export dialog reporting the theme it had not used.
|
|
106
|
+
* Every other named theme is a fixed preset that ignores config, which is
|
|
107
|
+
* the distinction this ternary keeps.
|
|
108
|
+
*/
|
|
109
|
+
getActiveThemeOptions(name) {
|
|
110
|
+
const all = this.allThemes();
|
|
111
|
+
const themeName = resolveThemeName(all, name ?? this.themeName);
|
|
112
|
+
const theme = all[themeName];
|
|
113
|
+
if (themeName !== 'default') {
|
|
114
|
+
return theme;
|
|
115
|
+
}
|
|
116
|
+
// shallow over the palette, which is how `resolvePalette` spreads the
|
|
117
|
+
// same two — `mode` and `primary` are siblings there, so both levels
|
|
118
|
+
// have to survive
|
|
119
|
+
const configTheme = getConf(self.jbrowse, 'theme');
|
|
120
|
+
return {
|
|
121
|
+
...theme,
|
|
122
|
+
...configTheme,
|
|
123
|
+
palette: { ...theme?.palette, ...configTheme.palette },
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
})
|
|
30
128
|
.actions(self => ({
|
|
129
|
+
/**
|
|
130
|
+
* #action
|
|
131
|
+
*/
|
|
31
132
|
setThemeName(name) {
|
|
32
133
|
self.sessionThemeName = name;
|
|
33
134
|
},
|
|
135
|
+
/**
|
|
136
|
+
* #action
|
|
137
|
+
* Point the session at light or dark, for a host that follows its own
|
|
138
|
+
* dark-mode state rather than offering JBrowse's theme menu. Satisfies
|
|
139
|
+
* `ThemeModeSession`, so `useSessionPalette` works against an app
|
|
140
|
+
* session and an embedded one alike.
|
|
141
|
+
*
|
|
142
|
+
* Expressed as a write to the config `theme` slot plus a return to the
|
|
143
|
+
* `default` theme, not as `setThemeName('darkStock')`. Only the
|
|
144
|
+
* `default` theme merges `configTheme.palette` (see `resolvePalette`),
|
|
145
|
+
* so selecting a stock theme would discard whatever the host passed as
|
|
146
|
+
* `configuration.theme` — their brand `primary`, say — the first time
|
|
147
|
+
* their toggle fired. Merging at both levels for the same reason:
|
|
148
|
+
* `theme` is a frozen slot, and `mode` and `primary` are siblings under
|
|
149
|
+
* `palette`.
|
|
150
|
+
*
|
|
151
|
+
* One write, not two: `themeOptions` is derived from the same slot and
|
|
152
|
+
* is what ships to the RPC worker, so the labels baked into a rendered
|
|
153
|
+
* image follow the mode along with what React draws.
|
|
154
|
+
*/
|
|
155
|
+
setThemeMode(mode) {
|
|
156
|
+
const { jbrowse } = asSession(self);
|
|
157
|
+
const theme = getConf(jbrowse, 'theme') ?? {};
|
|
158
|
+
setConf(jbrowse, 'theme', {
|
|
159
|
+
...theme,
|
|
160
|
+
palette: { ...theme.palette, mode },
|
|
161
|
+
});
|
|
162
|
+
self.sessionThemeName = 'default';
|
|
163
|
+
},
|
|
34
164
|
afterAttach() {
|
|
35
165
|
addDisposer(self, autorun(function themeNameAutorun() {
|
|
36
|
-
|
|
166
|
+
// persist the raw selection, not the coerced themeName, so a
|
|
167
|
+
// theme registered later isn't clobbered with 'default'
|
|
168
|
+
localStorageSetItem('themeName', self.sessionThemeName);
|
|
37
169
|
}, { name: 'ThemeName' }));
|
|
38
170
|
},
|
|
39
171
|
}));
|
|
40
172
|
}
|
|
173
|
+
/** Type guard for SessionWithThemes */
|
|
41
174
|
export function isSessionWithThemes(session) {
|
|
42
|
-
return 'theme' in session;
|
|
175
|
+
return isBaseSession(session) && 'theme' in session;
|
|
43
176
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
3
|
+
import type { BaseTrackConfig } from '@jbrowse/core/pluggableElementTypes';
|
|
4
|
+
import type { MenuItem } from '@jbrowse/core/ui';
|
|
5
|
+
import type { DialogComponentType } from '@jbrowse/core/util';
|
|
6
|
+
import type { TrackActionView } from '@jbrowse/core/util/types';
|
|
7
|
+
type TrackCopySnapshot = {
|
|
8
|
+
trackId: string;
|
|
9
|
+
name: string;
|
|
10
|
+
category?: unknown;
|
|
11
|
+
displays?: {
|
|
12
|
+
type: string;
|
|
13
|
+
displayId: string;
|
|
14
|
+
}[];
|
|
15
|
+
} & Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Clone a track config for "Copy track": snapshots the config, appends a unique
|
|
18
|
+
* timestamp suffix to trackId (so it doesn't collide with the original), tags
|
|
19
|
+
* " (copy)" on the name, and regenerates each displayId to the canonical
|
|
20
|
+
* `${trackId}-${type}` form baseTrackConfig auto-injects — keeping them unique
|
|
21
|
+
* (displayId is a types.identifier, so a collision would crash MST).
|
|
22
|
+
*/
|
|
23
|
+
export declare function copyTrackSnapshot(config: BaseTrackConfig, opts: {
|
|
24
|
+
clearCategory: boolean;
|
|
25
|
+
}): TrackCopySnapshot;
|
|
26
|
+
interface TrackActionSession<C> {
|
|
27
|
+
editConfiguration: (config: AnyConfigurationModel | {
|
|
28
|
+
trackId: string;
|
|
29
|
+
}, opts?: {
|
|
30
|
+
expandedDisplayId?: string;
|
|
31
|
+
}) => void;
|
|
32
|
+
publishTrackConf: (conf: C) => unknown;
|
|
33
|
+
deleteTrackConf: (conf: AnyConfigurationModel) => void;
|
|
34
|
+
resetTrackConfiguration?: (trackId: string) => void;
|
|
35
|
+
/**
|
|
36
|
+
* Declared here, though only the copy guard below reads it, so a session that
|
|
37
|
+
* loses the list loses the guard *loudly*. `namesTemporaryAssembly` takes an
|
|
38
|
+
* `unknown` session and answers false for one that holds no such list, which
|
|
39
|
+
* is right for a caller that genuinely has none and wrong for one that let it
|
|
40
|
+
* drop — and that second case is a copy action silently offered again.
|
|
41
|
+
*
|
|
42
|
+
* `unknown[]`, the spelling `AbstractSessionModel` already uses, and not the
|
|
43
|
+
* `{ name?: string }[]` the guard narrows to: that one is a weak type, so a
|
|
44
|
+
* real MST array of assembly configs — whose element type is an index
|
|
45
|
+
* signature that declares no `name` — shares no property with it and TS
|
|
46
|
+
* rejects the assignment. The shape belongs in the guard, which is the only
|
|
47
|
+
* thing that reads it.
|
|
48
|
+
*/
|
|
49
|
+
temporaryAssemblies?: unknown[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The shared Settings / Copy / Copy-and-open / Delete track actions. Each
|
|
53
|
+
* product supplies `makeCopy` (its own session-track/category rules) and
|
|
54
|
+
* `canEdit`; reference sequence tracks can't be copied or deleted. When
|
|
55
|
+
* `isSessionOverride` is set the track is a session edit shadowing an
|
|
56
|
+
* admin-owned config track, so the final action resets it instead of deleting.
|
|
57
|
+
*/
|
|
58
|
+
export declare function trackActionItems<C extends {
|
|
59
|
+
trackId: string;
|
|
60
|
+
}>({ session, config, view, canEdit, isSessionOverride, makeCopy, }: {
|
|
61
|
+
session: TrackActionSession<C>;
|
|
62
|
+
config: BaseTrackConfig;
|
|
63
|
+
view?: TrackActionView;
|
|
64
|
+
canEdit: boolean;
|
|
65
|
+
isSessionOverride?: boolean;
|
|
66
|
+
makeCopy: () => C;
|
|
67
|
+
}): MenuItem[];
|
|
68
|
+
interface SessionWithDialog {
|
|
69
|
+
queueDialog: (cb: (done: () => void) => [DialogComponentType, Record<string, unknown>]) => void;
|
|
70
|
+
}
|
|
71
|
+
type TrackConfig = AnyConfigurationModel | Record<string, unknown>;
|
|
72
|
+
/** "About track" menu item, shared by every product's track menu */
|
|
73
|
+
export declare function aboutTrackMenuItem(session: SessionWithDialog, config: TrackConfig): MenuItem;
|
|
74
|
+
/**
|
|
75
|
+
* plugin-contributed per-track items (`Core-extraTrackMenuItems`), surfaced in
|
|
76
|
+
* both the hierarchical selector and the in-view label menu so plugins reach
|
|
77
|
+
* every track menu consistently
|
|
78
|
+
*/
|
|
79
|
+
export declare function pluginExtraTrackItems(pluginManager: PluginManager, session: SessionWithDialog, config: AnyConfigurationModel, view?: TrackActionView): MenuItem[];
|
|
80
|
+
/**
|
|
81
|
+
* flattened track menu (About + raw actions) for the hierarchical track selector
|
|
82
|
+
*/
|
|
83
|
+
export declare function trackListMenuItems(session: SessionWithDialog, config: TrackConfig, actions: MenuItem[]): MenuItem[];
|
|
84
|
+
/**
|
|
85
|
+
* track menu with an "About track" item and a "Track actions" submenu, for the
|
|
86
|
+
* in-view track label menu
|
|
87
|
+
*/
|
|
88
|
+
export declare function trackActionMenuItems(session: SessionWithDialog, config: TrackConfig, actions: MenuItem[]): MenuItem[];
|
|
89
|
+
/**
|
|
90
|
+
* #stateModel TrackMenuItemsSessionMixin
|
|
91
|
+
*
|
|
92
|
+
* The two track-menu wrappers (`getTrackListMenuItems` for the hierarchical
|
|
93
|
+
* selector, `getTrackActionMenuItems` for the in-view label menu) shared by the
|
|
94
|
+
* full web and desktop sessions. Both are pure functions of `getTrackActions`,
|
|
95
|
+
* which each session supplies (web gates on edit rights; desktop adds indexing).
|
|
96
|
+
*/
|
|
97
|
+
export declare function TrackMenuItemsSessionMixin(pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{}, {
|
|
98
|
+
/**
|
|
99
|
+
* #method
|
|
100
|
+
* flattened menu items for use in hierarchical track selector
|
|
101
|
+
*/
|
|
102
|
+
getTrackListMenuItems(config: BaseTrackConfig, view?: TrackActionView): MenuItem[];
|
|
103
|
+
/**
|
|
104
|
+
* #method
|
|
105
|
+
* track menu with About + "Track actions" submenu for the in-view label
|
|
106
|
+
*/
|
|
107
|
+
getTrackActionMenuItems({ config, view, }: {
|
|
108
|
+
config: BaseTrackConfig;
|
|
109
|
+
view?: TrackActionView;
|
|
110
|
+
}): MenuItem[];
|
|
111
|
+
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|
|
112
|
+
export {};
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { lazy } from 'react';
|
|
2
|
+
import { InfoIcon } from '@jbrowse/core/ui/Icons';
|
|
3
|
+
import { buildExtraTrackMenuItems } from '@jbrowse/core/ui/buildExtraTrackMenuItems';
|
|
4
|
+
import { namesTemporaryAssembly } from '@jbrowse/core/util';
|
|
5
|
+
import { getSnapshot, isStateTreeNode, types } from '@jbrowse/mobx-state-tree';
|
|
6
|
+
import DeleteIcon from '@mui/icons-material/Delete';
|
|
7
|
+
import CopyIcon from '@mui/icons-material/FileCopy';
|
|
8
|
+
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
9
|
+
import SettingsIcon from '@mui/icons-material/Settings';
|
|
10
|
+
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
|
|
11
|
+
const AboutDialog = lazy(() => import('../ui/AboutDialog.js'));
|
|
12
|
+
/**
|
|
13
|
+
* Clone a track config for "Copy track": snapshots the config, appends a unique
|
|
14
|
+
* timestamp suffix to trackId (so it doesn't collide with the original), tags
|
|
15
|
+
* " (copy)" on the name, and regenerates each displayId to the canonical
|
|
16
|
+
* `${trackId}-${type}` form baseTrackConfig auto-injects — keeping them unique
|
|
17
|
+
* (displayId is a types.identifier, so a collision would crash MST).
|
|
18
|
+
*/
|
|
19
|
+
export function copyTrackSnapshot(config, opts) {
|
|
20
|
+
const snap = structuredClone(isStateTreeNode(config) ? getSnapshot(config) : config);
|
|
21
|
+
snap.trackId += `-${Date.now()}`;
|
|
22
|
+
snap.name += ' (copy)';
|
|
23
|
+
if (opts.clearCategory) {
|
|
24
|
+
snap.category = undefined;
|
|
25
|
+
}
|
|
26
|
+
for (const d of snap.displays ?? []) {
|
|
27
|
+
d.displayId = `${snap.trackId}-${d.type}`;
|
|
28
|
+
}
|
|
29
|
+
return snap;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The shared Settings / Copy / Copy-and-open / Delete track actions. Each
|
|
33
|
+
* product supplies `makeCopy` (its own session-track/category rules) and
|
|
34
|
+
* `canEdit`; reference sequence tracks can't be copied or deleted. When
|
|
35
|
+
* `isSessionOverride` is set the track is a session edit shadowing an
|
|
36
|
+
* admin-owned config track, so the final action resets it instead of deleting.
|
|
37
|
+
*/
|
|
38
|
+
export function trackActionItems({ session, config, view, canEdit, isSessionOverride, makeCopy, }) {
|
|
39
|
+
const isRefSeq = config.type === 'ReferenceSequenceTrack';
|
|
40
|
+
// A copy of a track drawn on an assembly this view synthesized is dead on
|
|
41
|
+
// arrival: the assembly goes back when the view closes, and the copy is left
|
|
42
|
+
// naming one that never existed anywhere else. Both destinations were wrong
|
|
43
|
+
// for it — `publishTrackConf` writes an admin's into the config.json every
|
|
44
|
+
// visitor is served, one per click since the copy's stamped trackId defeats
|
|
45
|
+
// the dedupe, and a non-admin's lands as the dead `sessionTracks` entry
|
|
46
|
+
// ADR-084 removed the sweep for. Neither is worth offering, so the item says
|
|
47
|
+
// so instead of producing garbage; the contract check on the adders
|
|
48
|
+
// is what found this.
|
|
49
|
+
//
|
|
50
|
+
// Greying a row tells nobody anything, and this reason is one nobody guesses:
|
|
51
|
+
// a refseq track explains its own greyed Copy, a synteny band that looks like
|
|
52
|
+
// every other track does not. `disabledHelpText` is what says it.
|
|
53
|
+
const isTemporary = namesTemporaryAssembly(session, config);
|
|
54
|
+
const copyRefusal = isRefSeq
|
|
55
|
+
? 'A reference sequence track cannot be copied.'
|
|
56
|
+
: isTemporary
|
|
57
|
+
? 'This track is drawn on an assembly this view synthesized, which goes away with the view — a copy of it would name an assembly nothing can resolve.'
|
|
58
|
+
: undefined;
|
|
59
|
+
// the display active in this view expands in the config editor, so the
|
|
60
|
+
// track's other (incompatible/inactive) displays start collapsed
|
|
61
|
+
const expandedDisplayId = view?.getActiveDisplayId?.(config.trackId);
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
// always available: editing a non-session (admin-owned) track applies
|
|
65
|
+
// in-memory for the current session even when the user can't persist it
|
|
66
|
+
label: 'Settings',
|
|
67
|
+
icon: SettingsIcon,
|
|
68
|
+
onClick: () => {
|
|
69
|
+
session.editConfiguration(config, { expandedDisplayId });
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
label: 'Copy track',
|
|
74
|
+
icon: CopyIcon,
|
|
75
|
+
disabled: isRefSeq || isTemporary,
|
|
76
|
+
disabledHelpText: copyRefusal,
|
|
77
|
+
onClick: () => {
|
|
78
|
+
session.publishTrackConf(makeCopy());
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
label: 'Copy and open track',
|
|
83
|
+
icon: OpenInNewIcon,
|
|
84
|
+
disabled: isRefSeq || isTemporary || !view,
|
|
85
|
+
disabledHelpText: copyRefusal ?? (view ? undefined : 'No view to open the copy in.'),
|
|
86
|
+
onClick: () => {
|
|
87
|
+
const snap = makeCopy();
|
|
88
|
+
if (session.publishTrackConf(snap)) {
|
|
89
|
+
void view.launchTrack(snap.trackId);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
isSessionOverride
|
|
94
|
+
? {
|
|
95
|
+
// a session edit of an admin-owned track: the underlying config track
|
|
96
|
+
// can't be deleted, so offer to discard the edits instead
|
|
97
|
+
label: 'Reset track settings',
|
|
98
|
+
icon: SettingsBackupRestoreIcon,
|
|
99
|
+
onClick: () => {
|
|
100
|
+
session.resetTrackConfiguration?.(config.trackId);
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
: {
|
|
104
|
+
label: 'Delete track',
|
|
105
|
+
icon: DeleteIcon,
|
|
106
|
+
disabled: !canEdit || isRefSeq,
|
|
107
|
+
onClick: () => {
|
|
108
|
+
session.deleteTrackConf(config);
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
/** "About track" menu item, shared by every product's track menu */
|
|
114
|
+
export function aboutTrackMenuItem(session, config) {
|
|
115
|
+
return {
|
|
116
|
+
label: 'About track',
|
|
117
|
+
icon: InfoIcon,
|
|
118
|
+
onClick: () => {
|
|
119
|
+
session.queueDialog(handleClose => [
|
|
120
|
+
AboutDialog,
|
|
121
|
+
{ config, session, handleClose },
|
|
122
|
+
]);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* plugin-contributed per-track items (`Core-extraTrackMenuItems`), surfaced in
|
|
128
|
+
* both the hierarchical selector and the in-view label menu so plugins reach
|
|
129
|
+
* every track menu consistently
|
|
130
|
+
*/
|
|
131
|
+
export function pluginExtraTrackItems(pluginManager, session, config, view) {
|
|
132
|
+
return buildExtraTrackMenuItems(pluginManager, {
|
|
133
|
+
session: session,
|
|
134
|
+
config,
|
|
135
|
+
view,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* flattened track menu (About + raw actions) for the hierarchical track selector
|
|
140
|
+
*/
|
|
141
|
+
export function trackListMenuItems(session, config, actions) {
|
|
142
|
+
return [aboutTrackMenuItem(session, config), ...actions];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* track menu with an "About track" item and a "Track actions" submenu, for the
|
|
146
|
+
* in-view track label menu
|
|
147
|
+
*/
|
|
148
|
+
export function trackActionMenuItems(session, config, actions) {
|
|
149
|
+
return [
|
|
150
|
+
{
|
|
151
|
+
...aboutTrackMenuItem(session, config),
|
|
152
|
+
priority: 1002,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: 'subMenu',
|
|
156
|
+
label: 'Track actions',
|
|
157
|
+
priority: 1001,
|
|
158
|
+
subMenu: actions,
|
|
159
|
+
},
|
|
160
|
+
{ type: 'divider' },
|
|
161
|
+
];
|
|
162
|
+
}
|
|
163
|
+
// (TrackMenuSessionMixin — the minimal embedded-view variant — lives in its own
|
|
164
|
+
// file so each documented state model is the sole one in its file, which the
|
|
165
|
+
// autogen docs generator requires.)
|
|
166
|
+
/**
|
|
167
|
+
* #stateModel TrackMenuItemsSessionMixin
|
|
168
|
+
*
|
|
169
|
+
* The two track-menu wrappers (`getTrackListMenuItems` for the hierarchical
|
|
170
|
+
* selector, `getTrackActionMenuItems` for the in-view label menu) shared by the
|
|
171
|
+
* full web and desktop sessions. Both are pure functions of `getTrackActions`,
|
|
172
|
+
* which each session supplies (web gates on edit rights; desktop adds indexing).
|
|
173
|
+
*/
|
|
174
|
+
export function TrackMenuItemsSessionMixin(pluginManager) {
|
|
175
|
+
return types.model('TrackMenuItemsSessionMixin', {}).views(s => {
|
|
176
|
+
const self = s;
|
|
177
|
+
return {
|
|
178
|
+
/**
|
|
179
|
+
* #method
|
|
180
|
+
* flattened menu items for use in hierarchical track selector
|
|
181
|
+
*/
|
|
182
|
+
getTrackListMenuItems(config, view) {
|
|
183
|
+
return [
|
|
184
|
+
...trackListMenuItems(self, config, self.getTrackActions(config, view)),
|
|
185
|
+
...pluginExtraTrackItems(pluginManager, self, config, view),
|
|
186
|
+
];
|
|
187
|
+
},
|
|
188
|
+
/**
|
|
189
|
+
* #method
|
|
190
|
+
* track menu with About + "Track actions" submenu for the in-view label
|
|
191
|
+
*/
|
|
192
|
+
getTrackActionMenuItems({ config, view, }) {
|
|
193
|
+
return trackActionMenuItems(self, config, [
|
|
194
|
+
...self.getTrackActions(config, view),
|
|
195
|
+
...pluginExtraTrackItems(pluginManager, self, config, view),
|
|
196
|
+
]);
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
3
|
+
import type { MenuItem } from '@jbrowse/core/ui';
|
|
4
|
+
import type { TrackActionView } from '@jbrowse/core/util/types';
|
|
5
|
+
/**
|
|
6
|
+
* #stateModel TrackMenuSessionMixin
|
|
7
|
+
*
|
|
8
|
+
* The minimal track menus used by the embedded react views, which have no
|
|
9
|
+
* track-editing actions to offer: just "About track" plus any plugin-contributed
|
|
10
|
+
* items (`Core-extraTrackMenuItems`). Mirrors the shape of the full
|
|
11
|
+
* `TrackMenuItemsSessionMixin` so both menu surfaces stay consistent across
|
|
12
|
+
* products, minus the Settings/Copy/Delete actions.
|
|
13
|
+
*/
|
|
14
|
+
export declare function TrackMenuSessionMixin(pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{}, {
|
|
15
|
+
/**
|
|
16
|
+
* #method
|
|
17
|
+
* flattened menu items for use in hierarchical track selector
|
|
18
|
+
*/
|
|
19
|
+
getTrackListMenuItems(config: AnyConfigurationModel, view?: TrackActionView): MenuItem[];
|
|
20
|
+
/**
|
|
21
|
+
* #method
|
|
22
|
+
*/
|
|
23
|
+
getTrackActionMenuItems({ config, view, }: {
|
|
24
|
+
config: AnyConfigurationModel;
|
|
25
|
+
view?: TrackActionView;
|
|
26
|
+
}): MenuItem[];
|
|
27
|
+
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|