@jbrowse/core 5.0.0-beta.1 → 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/README.md +42 -3
- package/esm/BaseFeatureWidget/SequenceFeatureDetails/test_data/NCDN.d.ts +2 -2
- package/esm/PluginLoader.d.ts +1 -0
- package/esm/PluginLoader.js +52 -8
- package/esm/PluginManager.d.ts +70 -17
- package/esm/PluginManager.js +151 -20
- package/esm/ReExports/modules.js +3 -53
- package/esm/ReExports/publicUtil.d.ts +1 -1
- package/esm/ReExports/publicUtil.js +1 -1
- package/esm/ReExports/sharedModules.d.ts +96 -0
- package/esm/ReExports/sharedModules.js +59 -0
- package/esm/ReExports/uiStub.d.ts +1 -0
- package/esm/ReExports/uiStub.js +13 -0
- package/esm/ReExports/workerModules.d.ts +2 -0
- package/esm/ReExports/workerModules.js +10 -0
- package/esm/configuration/configurationSchema.d.ts +5 -38
- package/esm/configuration/configurationSchema.js +53 -40
- package/esm/configuration/promotableDefaults.js +12 -6
- package/esm/configuration/promotableResolve.d.ts +1 -1
- package/esm/configuration/promotableResolve.js +1 -1
- package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.d.ts +12 -0
- package/esm/data_adapters/BaseAdapter/BaseFeatureDataAdapter.js +39 -0
- package/esm/data_adapters/BaseAdapter/featureDensity.d.ts +29 -0
- package/esm/data_adapters/BaseAdapter/featureDensity.js +17 -0
- package/esm/data_adapters/BaseAdapter/index.d.ts +1 -0
- package/esm/data_adapters/BaseAdapter/index.js +1 -0
- package/esm/data_adapters/BaseAdapter/stats.d.ts +33 -1
- package/esm/data_adapters/BaseAdapter/stats.js +52 -13
- package/esm/data_adapters/BaseAdapter/types.d.ts +1 -0
- package/esm/pluggableElementTypes/DisplayType.d.ts +9 -10
- package/esm/pluggableElementTypes/DisplayType.js +43 -8
- package/esm/pluggableElementTypes/ViewType.d.ts +25 -2
- package/esm/pluggableElementTypes/ViewType.js +75 -2
- package/esm/pluggableElementTypes/extendElementType.js +8 -3
- package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +41 -0
- package/esm/pluggableElementTypes/models/BaseDisplayModel.js +74 -1
- package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +21 -0
- package/esm/pluggableElementTypes/models/BaseTrackModel.js +37 -2
- package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +49 -0
- package/esm/pluggableElementTypes/models/BaseViewModel.js +39 -0
- package/esm/pluggableElementTypes/models/assertDisplayContract.d.ts +10 -7
- package/esm/pluggableElementTypes/models/assertDisplayContract.js +23 -30
- package/esm/rpc/RpcRegistry.d.ts +10 -0
- package/esm/rpc/byteBudget.d.ts +9 -0
- package/esm/rpc/coreRpcMethods.d.ts +1 -0
- package/esm/rpc/coreRpcMethods.js +1 -0
- package/esm/rpc/methods/CoreGetFeatureDensity.d.ts +11 -0
- package/esm/rpc/methods/CoreGetFeatureDensity.js +22 -0
- package/esm/svg/svgReady.d.ts +1 -0
- package/esm/ui/CascadingMenu.js +30 -8
- package/esm/ui/LoadingOverlay.d.ts +14 -0
- package/esm/ui/LoadingOverlay.js +1 -1
- package/esm/ui/MenuTypes.d.ts +11 -2
- package/esm/ui/MenuTypes.js +3 -0
- package/esm/ui/PaletteContext.d.ts +11 -4
- package/esm/ui/PaletteContext.js +34 -5
- package/esm/ui/PinAdornment.js +12 -6
- package/esm/ui/index.d.ts +1 -1
- package/esm/ui/index.js +1 -1
- package/esm/ui/launchViewMenu.js +7 -1
- package/esm/ui/legendMenuItem.d.ts +5 -5
- package/esm/ui/legendMenuItem.js +5 -5
- package/esm/ui/makeSizeMenu.js +1 -1
- package/esm/ui/menuItems.d.ts +1 -1
- package/esm/ui/menuItems.js +1 -1
- package/esm/ui/promotableMenuItems.js +8 -6
- package/esm/ui/promotablePinCoverage.js +2 -1
- package/esm/ui/toggleMenuItems.d.ts +1 -3
- package/esm/util/addAndShowTrack.d.ts +1 -1
- package/esm/util/addAndShowTrack.js +5 -1
- package/esm/util/addTrackFromWidget.js +2 -2
- package/esm/util/cacheBudgets.d.ts +22 -19
- package/esm/util/cacheBudgets.js +22 -19
- package/esm/util/createStopTokenRotation.d.ts +1 -0
- package/esm/util/createStopTokenRotation.js +7 -27
- package/esm/util/fetchContext.d.ts +2 -2
- package/esm/util/fetchContext.js +2 -2
- package/esm/util/fetchPhases.d.ts +9 -9
- package/esm/util/hooks.d.ts +8 -0
- package/esm/util/hooks.js +27 -0
- package/esm/util/index.d.ts +2 -3
- package/esm/util/index.js +2 -3
- package/esm/util/installFetch.d.ts +49 -23
- package/esm/util/installFetch.js +49 -21
- package/esm/util/installInitAutorun.d.ts +8 -8
- package/esm/util/installInitAutorun.js +15 -15
- package/esm/util/lazyAfterAttach.d.ts +13 -0
- package/esm/util/lazyAfterAttach.js +31 -0
- package/esm/util/legendCandidates.d.ts +50 -0
- package/esm/util/legendCandidates.js +87 -0
- package/esm/util/mstUtils.d.ts +12 -1
- package/esm/util/mstUtils.js +20 -2
- package/esm/util/pluginStore.d.ts +1 -0
- package/esm/util/pluginStore.js +8 -1
- package/esm/util/range.d.ts +13 -9
- package/esm/util/range.js +17 -8
- package/esm/util/rxjs.d.ts +16 -0
- package/esm/util/rxjs.js +49 -0
- package/esm/util/tabix.d.ts +10 -4
- package/esm/util/tabix.js +11 -5
- package/esm/util/tracks.d.ts +48 -0
- package/esm/util/tracks.js +192 -66
- package/esm/util/types/index.d.ts +48 -5
- package/esm/util/types/index.js +3 -0
- package/esm/util/unknownSnapshotKeys.d.ts +36 -0
- package/esm/util/unknownSnapshotKeys.js +69 -0
- package/esm/util/viewStatus.d.ts +23 -0
- package/esm/util/viewStatus.js +17 -0
- package/esm/util/whenViewSettled.d.ts +17 -4
- package/esm/util/whenViewSettled.js +19 -6
- package/esm/util/withLaunchInput.d.ts +123 -0
- package/esm/util/withLaunchInput.js +228 -0
- package/package.json +12 -9
- package/esm/util/applyInitSettings.d.ts +0 -79
- package/esm/util/applyInitSettings.js +0 -91
package/esm/ui/MenuTypes.d.ts
CHANGED
|
@@ -127,11 +127,20 @@ export interface RadioMenuItem extends BaseMenuItem {
|
|
|
127
127
|
checked: boolean;
|
|
128
128
|
onClick: MenuItemClickHandler;
|
|
129
129
|
}
|
|
130
|
-
/**
|
|
130
|
+
/**
|
|
131
|
+
* #menuItem subMenu | nests another `MenuItem[]`, or a function building one, to any depth
|
|
132
|
+
*
|
|
133
|
+
* The function form is called when the submenu opens (or a walker asks for its
|
|
134
|
+
* rows), not when the parent menu is built — for a submenu whose rows cost a
|
|
135
|
+
* scan of the display's data to name. Read the field through `resolveSubMenu`
|
|
136
|
+
* rather than by hand, so the two forms stay one thing to the renderer, the
|
|
137
|
+
* pin-coverage walk and every test helper that flattens a menu.
|
|
138
|
+
*/
|
|
131
139
|
export interface SubMenuItem extends BaseMenuItem {
|
|
132
140
|
type?: 'subMenu';
|
|
133
|
-
subMenu: MenuItem[];
|
|
141
|
+
subMenu: MenuItem[] | (() => MenuItem[]);
|
|
134
142
|
}
|
|
143
|
+
export declare function resolveSubMenu(item: SubMenuItem): MenuItem[];
|
|
135
144
|
/**
|
|
136
145
|
* #menuItem custom | renders arbitrary React content inline, e.g. a slider
|
|
137
146
|
*
|
package/esm/ui/MenuTypes.js
CHANGED
|
@@ -40,8 +40,9 @@ export interface ThemeModeSession {
|
|
|
40
40
|
* hook and the provider in one.
|
|
41
41
|
*
|
|
42
42
|
* How the host knows its own mode is the host's business — an attribute on
|
|
43
|
-
* `<html>`, a
|
|
44
|
-
* and
|
|
43
|
+
* `<html>`, a design-system context, a toggle in its own state. Pass no mode
|
|
44
|
+
* and JBrowse follows `prefers-color-scheme` instead, tracking OS changes; a
|
|
45
|
+
* host whose mode is only ever the OS preference has nothing to write.
|
|
45
46
|
*
|
|
46
47
|
* **Both halves are load-bearing, which is why the pairing is published as a
|
|
47
48
|
* component.** The palette is what *React* draws with; the config `theme` slot
|
|
@@ -55,7 +56,7 @@ export interface ThemeModeSession {
|
|
|
55
56
|
* the previous palette. That is a frame on a canvas that is about to be
|
|
56
57
|
* redrawn anyway; it is not somewhere to add a suspense boundary.
|
|
57
58
|
*/
|
|
58
|
-
export declare function useSessionPalette(session: ThemeModeSession, mode
|
|
59
|
+
export declare function useSessionPalette(session: ThemeModeSession, mode?: 'light' | 'dark'): JBrowsePalette;
|
|
59
60
|
/**
|
|
60
61
|
* #api
|
|
61
62
|
* Make JBrowse follow the host's light/dark state — the whole of it, in one
|
|
@@ -67,6 +68,12 @@ export declare function useSessionPalette(session: ThemeModeSession, mode: 'ligh
|
|
|
67
68
|
* </SessionPaletteProvider>
|
|
68
69
|
* ```
|
|
69
70
|
*
|
|
71
|
+
* `mode` is optional. Left out, JBrowse follows `prefers-color-scheme` and
|
|
72
|
+
* re-themes when the OS preference changes, through the same session write an
|
|
73
|
+
* explicit mode takes — so a host whose dark mode *is* the OS preference mounts
|
|
74
|
+
* this with a session and nothing else. Pass a mode as soon as the host has a
|
|
75
|
+
* toggle of its own, since the media query cannot see it.
|
|
76
|
+
*
|
|
70
77
|
* A component rather than a documented pair of calls because the pair has a
|
|
71
78
|
* half that can be left out with nothing to show for it. `PaletteProvider` is
|
|
72
79
|
* the name a host reaches for, and it colors the React side alone; the session
|
|
@@ -80,6 +87,6 @@ export declare function useSessionPalette(session: ThemeModeSession, mode: 'ligh
|
|
|
80
87
|
*/
|
|
81
88
|
export declare function SessionPaletteProvider({ session, mode, children, }: {
|
|
82
89
|
session: ThemeModeSession;
|
|
83
|
-
mode
|
|
90
|
+
mode?: 'light' | 'dark';
|
|
84
91
|
children: ReactNode;
|
|
85
92
|
}): import("react").JSX.Element;
|
package/esm/ui/PaletteContext.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, use, useEffect, useMemo } from 'react';
|
|
2
|
+
import { createContext, use, useEffect, useMemo, useSyncExternalStore, } from 'react';
|
|
3
3
|
import { defaultStyleTheme } from './styleTheme.js';
|
|
4
4
|
/**
|
|
5
5
|
* How a React component asks for JBrowse's design tokens.
|
|
@@ -47,6 +47,27 @@ export function useStyleTheme() {
|
|
|
47
47
|
export function usePalette() {
|
|
48
48
|
return useStyleTheme().palette;
|
|
49
49
|
}
|
|
50
|
+
function darkSchemeQuery() {
|
|
51
|
+
return typeof window.matchMedia === 'function'
|
|
52
|
+
? window.matchMedia('(prefers-color-scheme: dark)')
|
|
53
|
+
: undefined;
|
|
54
|
+
}
|
|
55
|
+
function subscribeToColorScheme(onChange) {
|
|
56
|
+
const media = darkSchemeQuery();
|
|
57
|
+
media?.addEventListener('change', onChange);
|
|
58
|
+
return () => {
|
|
59
|
+
media?.removeEventListener('change', onChange);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function readColorScheme() {
|
|
63
|
+
return darkSchemeQuery()?.matches ? 'dark' : 'light';
|
|
64
|
+
}
|
|
65
|
+
const alwaysLight = () => 'light';
|
|
66
|
+
const subscribeToNothing = () => () => { };
|
|
67
|
+
function useResolvedMode(mode) {
|
|
68
|
+
const systemMode = useSyncExternalStore(mode === undefined ? subscribeToColorScheme : subscribeToNothing, mode === undefined ? readColorScheme : alwaysLight, alwaysLight);
|
|
69
|
+
return mode === undefined ? systemMode : mode;
|
|
70
|
+
}
|
|
50
71
|
/**
|
|
51
72
|
* Point a session at light or dark and get back the palette it resolves to,
|
|
52
73
|
* for a host that wants the palette itself — to read a color out of, or to
|
|
@@ -55,8 +76,9 @@ export function usePalette() {
|
|
|
55
76
|
* hook and the provider in one.
|
|
56
77
|
*
|
|
57
78
|
* How the host knows its own mode is the host's business — an attribute on
|
|
58
|
-
* `<html>`, a
|
|
59
|
-
* and
|
|
79
|
+
* `<html>`, a design-system context, a toggle in its own state. Pass no mode
|
|
80
|
+
* and JBrowse follows `prefers-color-scheme` instead, tracking OS changes; a
|
|
81
|
+
* host whose mode is only ever the OS preference has nothing to write.
|
|
60
82
|
*
|
|
61
83
|
* **Both halves are load-bearing, which is why the pairing is published as a
|
|
62
84
|
* component.** The palette is what *React* draws with; the config `theme` slot
|
|
@@ -71,9 +93,10 @@ export function usePalette() {
|
|
|
71
93
|
* redrawn anyway; it is not somewhere to add a suspense boundary.
|
|
72
94
|
*/
|
|
73
95
|
export function useSessionPalette(session, mode) {
|
|
96
|
+
const resolved = useResolvedMode(mode);
|
|
74
97
|
useEffect(() => {
|
|
75
|
-
session.setThemeMode(
|
|
76
|
-
}, [session,
|
|
98
|
+
session.setThemeMode(resolved);
|
|
99
|
+
}, [session, resolved]);
|
|
77
100
|
return session.palette;
|
|
78
101
|
}
|
|
79
102
|
/**
|
|
@@ -87,6 +110,12 @@ export function useSessionPalette(session, mode) {
|
|
|
87
110
|
* </SessionPaletteProvider>
|
|
88
111
|
* ```
|
|
89
112
|
*
|
|
113
|
+
* `mode` is optional. Left out, JBrowse follows `prefers-color-scheme` and
|
|
114
|
+
* re-themes when the OS preference changes, through the same session write an
|
|
115
|
+
* explicit mode takes — so a host whose dark mode *is* the OS preference mounts
|
|
116
|
+
* this with a session and nothing else. Pass a mode as soon as the host has a
|
|
117
|
+
* toggle of its own, since the media query cannot see it.
|
|
118
|
+
*
|
|
90
119
|
* A component rather than a documented pair of calls because the pair has a
|
|
91
120
|
* half that can be left out with nothing to show for it. `PaletteProvider` is
|
|
92
121
|
* the name a host reaches for, and it colors the React side alone; the session
|
package/esm/ui/PinAdornment.js
CHANGED
|
@@ -38,25 +38,31 @@ function pinPredicate(onValue) {
|
|
|
38
38
|
}
|
|
39
39
|
// The click, which is not the state the pin draws: an outline pin applies the
|
|
40
40
|
// value to the open tracks, a filled one clears the default it stands for.
|
|
41
|
+
//
|
|
42
|
+
// The aria-label carries "of this type" wherever the tooltip does: it used to
|
|
43
|
+
// stop at "for all open tracks", stating a wider blast radius than the control
|
|
44
|
+
// has.
|
|
41
45
|
function pinCopy(label, onValue, isDefault) {
|
|
42
46
|
return isDefault
|
|
43
47
|
? {
|
|
44
48
|
title: `${label} is ${pinPredicate(onValue)} for all tracks of this type (click to clear)`,
|
|
45
|
-
ariaLabel: `clear the default for ${label}`,
|
|
49
|
+
ariaLabel: `clear the default for ${label} for all tracks of this type`,
|
|
46
50
|
}
|
|
47
51
|
: typeof onValue === 'boolean'
|
|
48
52
|
? {
|
|
49
53
|
title: `Turn ${label} ${onValue ? 'on' : 'off'} for all open tracks of this type`,
|
|
50
|
-
ariaLabel: `turn ${label} ${onValue ? 'on' : 'off'} for all open tracks`,
|
|
54
|
+
ariaLabel: `turn ${label} ${onValue ? 'on' : 'off'} for all open tracks of this type`,
|
|
51
55
|
}
|
|
52
56
|
: {
|
|
53
57
|
title: `Apply ${label} to all open tracks of this type`,
|
|
54
|
-
ariaLabel: `apply ${label} to all open tracks`,
|
|
58
|
+
ariaLabel: `apply ${label} to all open tracks of this type`,
|
|
55
59
|
};
|
|
56
60
|
}
|
|
57
|
-
// Trailing pin for a promotable setting,
|
|
58
|
-
// `
|
|
59
|
-
//
|
|
61
|
+
// Trailing pin for a promotable setting, drawn beside the value check from the
|
|
62
|
+
// row's `pin` declaration (`menuItemAdornment` builds this; a row never
|
|
63
|
+
// constructs it, which is what keeps MUI out of the eager menu-builder graph). A
|
|
64
|
+
// ToggleButton (native button a11y + a clear selected tint) with a pin —
|
|
65
|
+
// distinct from the value checkbox.
|
|
60
66
|
//
|
|
61
67
|
// **The click and the state are two different things.** One click writes the
|
|
62
68
|
// value into every open track of this display type and raises a snackbar
|
package/esm/ui/index.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ export type { SlotExtensionPointName, SlotProps, WrapperProps, } from './wrapCom
|
|
|
70
70
|
export { matchesTrackSelector } from './extensionSelectors.ts';
|
|
71
71
|
export type { TrackSelector } from './extensionSelectors.ts';
|
|
72
72
|
export type { BaseMenuItem, CheckboxMenuItem, ClickableMenuItem, CustomMenuItem, MenuDivider, MenuItem, MenuItemClickHandler, MenuItemsGetter, MenuSubHeader, NormalMenuItem, RadioMenuItem, SubMenuItem, } from './MenuTypes.ts';
|
|
73
|
-
export { staysOpenOnClick } from './MenuTypes.ts';
|
|
73
|
+
export { resolveSubMenu, staysOpenOnClick } from './MenuTypes.ts';
|
|
74
74
|
export { LAUNCH_LABEL, pushIntoSubMenu, pushLaunchViewMenuItem, } from './launchViewMenu.ts';
|
|
75
75
|
export { launchTargetsMenuItem } from './launchTargetsMenuItem.ts';
|
|
76
76
|
export { default as Crosshairs } from './Crosshairs.tsx';
|
package/esm/ui/index.js
CHANGED
|
@@ -65,7 +65,7 @@ export { default as PluggableElements } from './PluggableElements.js';
|
|
|
65
65
|
export { addExtensionElement } from './addExtensionElement.js';
|
|
66
66
|
export { wrapComponent } from './wrapComponent.js';
|
|
67
67
|
export { matchesTrackSelector } from './extensionSelectors.js';
|
|
68
|
-
export { staysOpenOnClick } from './MenuTypes.js';
|
|
68
|
+
export { resolveSubMenu, staysOpenOnClick } from './MenuTypes.js';
|
|
69
69
|
export { LAUNCH_LABEL, pushIntoSubMenu, pushLaunchViewMenuItem, } from './launchViewMenu.js';
|
|
70
70
|
export { launchTargetsMenuItem } from './launchTargetsMenuItem.js';
|
|
71
71
|
export { default as Crosshairs } from './Crosshairs.js';
|
package/esm/ui/launchViewMenu.js
CHANGED
|
@@ -10,7 +10,13 @@ export const LAUNCH_LABEL = 'Launch';
|
|
|
10
10
|
export function pushIntoSubMenu(items, label, item) {
|
|
11
11
|
const existing = items.find((i) => i.type === 'subMenu' && i.label === label);
|
|
12
12
|
if (existing) {
|
|
13
|
-
existing
|
|
13
|
+
const { subMenu } = existing;
|
|
14
|
+
if (typeof subMenu === 'function') {
|
|
15
|
+
existing.subMenu = () => [...subMenu(), item];
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
subMenu.push(item);
|
|
19
|
+
}
|
|
14
20
|
}
|
|
15
21
|
else {
|
|
16
22
|
items.push({ label, type: 'subMenu', subMenu: [item] });
|
|
@@ -14,11 +14,11 @@ import type { MenuItem } from './MenuTypes.ts';
|
|
|
14
14
|
* values legitimately differ (a Hi-C color scale is off by default, a variant
|
|
15
15
|
* genotype key on), and their descriptions describe genuinely different legends.
|
|
16
16
|
* What they now share is being *promotable* — pass `opts.pin` and the row gains
|
|
17
|
-
* the pin that
|
|
18
|
-
*
|
|
19
|
-
* and gets it from
|
|
20
|
-
*
|
|
21
|
-
* over it are not.
|
|
17
|
+
* the pin that applies the current state to every open track of this display
|
|
18
|
+
* type, offering the display-type default as its snackbar's one action. Every
|
|
19
|
+
* display whose legend is backed by a config slot passes one, and gets it from
|
|
20
|
+
* `LegendMixin`'s `showLegendDisplayTypeDefault` rather than calling `makePin`
|
|
21
|
+
* itself: the slot is the per-display half, the accessors over it are not.
|
|
22
22
|
*
|
|
23
23
|
* `pin` is optional because two callers have no slot to promote: the Manhattan
|
|
24
24
|
* plot's LD legend and `LinearBasicDisplay`'s color key are a volatile and a
|
package/esm/ui/legendMenuItem.js
CHANGED
|
@@ -14,11 +14,11 @@ import { checkboxItem } from './toggleMenuItems.js';
|
|
|
14
14
|
* values legitimately differ (a Hi-C color scale is off by default, a variant
|
|
15
15
|
* genotype key on), and their descriptions describe genuinely different legends.
|
|
16
16
|
* What they now share is being *promotable* — pass `opts.pin` and the row gains
|
|
17
|
-
* the pin that
|
|
18
|
-
*
|
|
19
|
-
* and gets it from
|
|
20
|
-
*
|
|
21
|
-
* over it are not.
|
|
17
|
+
* the pin that applies the current state to every open track of this display
|
|
18
|
+
* type, offering the display-type default as its snackbar's one action. Every
|
|
19
|
+
* display whose legend is backed by a config slot passes one, and gets it from
|
|
20
|
+
* `LegendMixin`'s `showLegendDisplayTypeDefault` rather than calling `makePin`
|
|
21
|
+
* itself: the slot is the per-display half, the accessors over it are not.
|
|
22
22
|
*
|
|
23
23
|
* `pin` is optional because two callers have no slot to promote: the Manhattan
|
|
24
24
|
* plot's LD legend and `LinearBasicDisplay`'s color key are a volatile and a
|
package/esm/ui/makeSizeMenu.js
CHANGED
|
@@ -51,7 +51,7 @@ export function makeSizeSubMenu(opts) {
|
|
|
51
51
|
// The same row over a **promotable** slot, which derives both halves the plain
|
|
52
52
|
// form takes by hand — the reset button's enablement (`isSlotCustomized`) and
|
|
53
53
|
// the "default for all tracks of this type" pin — from the one slot name. A
|
|
54
|
-
// slider has no fixed on-value, so the pin
|
|
54
|
+
// slider has no fixed on-value, so the pin carries whatever the track is
|
|
55
55
|
// currently showing.
|
|
56
56
|
//
|
|
57
57
|
// Naming the slot once is the point: the call sites used to spell it twice, in
|
package/esm/ui/menuItems.d.ts
CHANGED
|
@@ -24,5 +24,5 @@ export { makeRadioSubMenu } from './radioSubMenu.ts';
|
|
|
24
24
|
export { showLegendCheckboxItem } from './legendMenuItem.ts';
|
|
25
25
|
export { promotableRadioItem, promotableRadioItems, promotableToggleItem, } from './promotableMenuItems.ts';
|
|
26
26
|
export type { RadioOption, SettingRowOptions } from './toggleMenuItems.ts';
|
|
27
|
-
export { staysOpenOnClick } from './MenuTypes.ts';
|
|
27
|
+
export { resolveSubMenu, staysOpenOnClick } from './MenuTypes.ts';
|
|
28
28
|
export type { BaseMenuItem, CheckboxMenuItem, ClickableMenuItem, CustomMenuItem, MenuDivider, MenuItem, MenuItemClickHandler, MenuItemPin, MenuItemsGetter, MenuSubHeader, NormalMenuItem, RadioMenuItem, SubMenuItem, } from './MenuTypes.ts';
|
package/esm/ui/menuItems.js
CHANGED
|
@@ -23,4 +23,4 @@ export { withHint } from './menuLabels.js';
|
|
|
23
23
|
export { makeRadioSubMenu } from './radioSubMenu.js';
|
|
24
24
|
export { showLegendCheckboxItem } from './legendMenuItem.js';
|
|
25
25
|
export { promotableRadioItem, promotableRadioItems, promotableToggleItem, } from './promotableMenuItems.js';
|
|
26
|
-
export { staysOpenOnClick } from './MenuTypes.js';
|
|
26
|
+
export { resolveSubMenu, staysOpenOnClick } from './MenuTypes.js';
|
|
@@ -10,10 +10,10 @@ import { checkboxItem, radioItem, radioItems } from './toggleMenuItems.js';
|
|
|
10
10
|
// written that way — `checkboxItem` / `radioItems` build the row — so the
|
|
11
11
|
// promotable and plain forms of a row can differ only by the pin.
|
|
12
12
|
// A promotable setting as one native checkbox menu row: the value toggles the
|
|
13
|
-
// track (inheriting native hover/sizing/keyboard), and a trailing pin
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
13
|
+
// track (inheriting native hover/sizing/keyboard), and a trailing pin applies
|
|
14
|
+
// this value to every open track of the display type, offering the display-type
|
|
15
|
+
// default as its snackbar's one action. Always shown so the capability is
|
|
16
|
+
// discoverable.
|
|
17
17
|
//
|
|
18
18
|
// **The row itself is `checkboxItem`'s**, so the two builders can only ever
|
|
19
19
|
// differ by the pin. They used to be two literals, and had already drifted: this
|
|
@@ -61,8 +61,10 @@ export function promotableRadioItem({ label, checked, onClick, pin, ...opts }) {
|
|
|
61
61
|
}
|
|
62
62
|
// A whole promotable radio group: `radioItems` plus one pin per option. The
|
|
63
63
|
// plural form of the builder above, and the one to reach for on a group whose
|
|
64
|
-
// options differ only in their value — which is all
|
|
65
|
-
// (`heightMode`, `displayMode`, `
|
|
64
|
+
// options differ only in their value — which is all five of them today
|
|
65
|
+
// (`heightMode`, `displayMode`, `showLabels`, `subfeatureLabels`,
|
|
66
|
+
// `sashimiArcsMode`; the two label groups reach it through canvas's
|
|
67
|
+
// `inlineRadioGroup`).
|
|
66
68
|
//
|
|
67
69
|
// **Every option in a group gets a pin, the `promotedBase` value included** —
|
|
68
70
|
// once a non-base value is promoted, pinning the base back is the only per-value
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* result against a baseline.
|
|
24
24
|
*/
|
|
25
25
|
import { promotableSlotNames } from '../configuration/promotableSlots.js';
|
|
26
|
+
import { resolveSubMenu } from './MenuTypes.js';
|
|
26
27
|
/**
|
|
27
28
|
* The registered display types whose config schema declares a promotable slot,
|
|
28
29
|
* sorted — the set a pin-coverage check has to reach, answered off the
|
|
@@ -61,7 +62,7 @@ export function pinnedSlots(items) {
|
|
|
61
62
|
found.add(item.pin.control.slot);
|
|
62
63
|
}
|
|
63
64
|
if ('subMenu' in item) {
|
|
64
|
-
walk(item
|
|
65
|
+
walk(resolveSubMenu(item));
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
};
|
|
@@ -26,11 +26,9 @@ export declare function checkboxItem(label: string, checked: boolean, onToggle:
|
|
|
26
26
|
export declare function toggleItem(label: string, value: boolean, setValue: (value: boolean) => void, opts?: SettingRowOptions): CheckboxMenuItem;
|
|
27
27
|
/** #menuBuilder radioItem | one radio setting row; the singular of `radioItems` */
|
|
28
28
|
export declare function radioItem(label: string, checked: boolean, onClick: () => void, opts?: SettingRowOptions): RadioMenuItem;
|
|
29
|
-
export interface RadioOption<T extends string> {
|
|
29
|
+
export interface RadioOption<T extends string> extends SettingRowOptions {
|
|
30
30
|
value: T;
|
|
31
31
|
label: string;
|
|
32
|
-
helpText?: string;
|
|
33
|
-
keepMenuOpen?: boolean;
|
|
34
32
|
}
|
|
35
33
|
/** #menuBuilder radioItems | a radio group, one row per option */
|
|
36
34
|
export declare function radioItems<T extends string>(options: readonly RadioOption<T>[], current: T | undefined, setMode: (m: T) => void): RadioMenuItem[];
|
|
@@ -15,7 +15,7 @@ import type { SessionWithAddSessionTrack } from './types/index.ts';
|
|
|
15
15
|
export declare function addAndShowTrack(session: SessionWithAddSessionTrack, conf: Parameters<SessionWithAddSessionTrack['addSessionTrackConf']>[0] & {
|
|
16
16
|
trackId: string;
|
|
17
17
|
}, view?: {
|
|
18
|
-
|
|
18
|
+
launchTrack: (trackId: string) => Promise<unknown>;
|
|
19
19
|
}): (import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
|
|
20
20
|
setSubschema(slotName: string, data: Record<string, unknown>): any;
|
|
21
21
|
setSlot(slotName: string, value: unknown): void;
|
|
@@ -16,8 +16,12 @@ export function addAndShowTrack(session, conf, view) {
|
|
|
16
16
|
// addSessionTrackConf already notified on an invalid conf; showing a trackId
|
|
17
17
|
// that was never added would only add a second "Could not resolve identifier"
|
|
18
18
|
// snackbar
|
|
19
|
+
//
|
|
20
|
+
// `launchTrack`, voided: the display's state model may still be a dynamic
|
|
21
|
+
// import away, and every caller here is a menu item or a dialog button whose
|
|
22
|
+
// return value is the added config rather than the opened track
|
|
19
23
|
if (added) {
|
|
20
|
-
view?.
|
|
24
|
+
void view?.launchTrack(conf.trackId);
|
|
21
25
|
}
|
|
22
26
|
return added;
|
|
23
27
|
}
|
|
@@ -68,7 +68,7 @@ export function addTrackFromWidget({ model, session, conf, }) {
|
|
|
68
68
|
session.notify(`Could not add "${conf.name ?? conf.trackId}": the view it was being added to has closed, and it uses an assembly that only that view had.`, 'warning');
|
|
69
69
|
return undefined;
|
|
70
70
|
}
|
|
71
|
-
trackContainer.
|
|
71
|
+
void trackContainer.launchTrack(conf.trackId, {}, {}, conf);
|
|
72
72
|
finishAddTrack(model, session);
|
|
73
73
|
return conf;
|
|
74
74
|
}
|
|
@@ -77,7 +77,7 @@ export function addTrackFromWidget({ model, session, conf, }) {
|
|
|
77
77
|
return undefined;
|
|
78
78
|
}
|
|
79
79
|
if (containerDisplaysAssembly(trackContainer, conf.assemblyNames, session.assemblyManager)) {
|
|
80
|
-
trackContainer?.
|
|
80
|
+
void trackContainer?.launchTrack(conf.trackId);
|
|
81
81
|
}
|
|
82
82
|
else {
|
|
83
83
|
const assemblies = conf.assemblyNames?.filter(a => !!a) ?? [];
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { SharedBudget } from '@gmod/shared-read-cache';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* in this JS context — so one per RPC worker, plus one on the main
|
|
5
|
-
* which is the scope that actually runs out of memory.
|
|
3
|
+
* The ceiling on what the indexed-format libraries retain, shared by every
|
|
4
|
+
* adapter in this JS context — so one per RPC worker, plus one on the main
|
|
5
|
+
* thread, which is the scope that actually runs out of memory.
|
|
6
6
|
*
|
|
7
|
-
* ## Why
|
|
7
|
+
* ## Why this is not per file
|
|
8
8
|
*
|
|
9
9
|
* `@gmod/bam`, `@gmod/tabix` and `@gmod/cram` each take a per-file budget,
|
|
10
10
|
* sized so that a single track panning never falls off the cliff where a cache
|
|
@@ -29,24 +29,27 @@ import { SharedBudget } from '@gmod/shared-read-cache';
|
|
|
29
29
|
* at hand their space to the one being panned, so the active track keeps a
|
|
30
30
|
* whole working set however many are open.
|
|
31
31
|
*
|
|
32
|
-
* ## The
|
|
32
|
+
* ## The number is not new
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
34
|
+
* 1 GB is the libraries' own per-file default (`DEFAULT_MAX_CACHE_BYTES` in
|
|
35
|
+
* both `@gmod/bam` and `@gmod/cram`), moved from per-file to per-context. That
|
|
36
|
+
* makes this a strict tightening — retention can only fall — and avoids
|
|
37
|
+
* inventing a number that would need its own justification. With one file open
|
|
38
|
+
* the two ceilings coincide; from the second file on, this one is the one that
|
|
39
|
+
* binds. The budget still has to clear one query's working set or the cliff
|
|
40
|
+
* returns, and on that workload 512 MB shared across three tracks already cost
|
|
41
|
+
* 43 refills where 1 GB cost 4.
|
|
40
42
|
*
|
|
41
|
-
* ##
|
|
43
|
+
* ## One budget, because every member weighs bytes
|
|
42
44
|
*
|
|
43
45
|
* `SharedBudget.total` is a sum over its members, so they must all weigh in the
|
|
44
|
-
* same unit
|
|
45
|
-
* `@gmod/
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* same unit, and `sizeOf` is opaque, so nothing could catch a mismatch at
|
|
47
|
+
* runtime. `@gmod/bam` and `@gmod/tabix` weigh decompressed bytes, and since 14
|
|
48
|
+
* `@gmod/cram` weighs a decoded slice by `DecodedSlice.byteLength` — its typed
|
|
49
|
+
* columns exactly and its strings by estimate, within 1–8% of the measured heap
|
|
50
|
+
* (its ADR 0013). Before that a CRAM slice weighed its record count, which is
|
|
51
|
+
* why this used to be two budgets named for their units: a million records
|
|
52
|
+
* could not be added to bytes, and never bound long-read data either, where a
|
|
53
|
+
* 6 MB slice counted as 37.
|
|
49
54
|
*/
|
|
50
55
|
export declare const decompressedBytesBudget: SharedBudget;
|
|
51
|
-
/** See {@link decompressedBytesBudget}. Records, not bytes — CRAM only. */
|
|
52
|
-
export declare const decodedRecordsBudget: SharedBudget;
|
package/esm/util/cacheBudgets.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { SharedBudget } from '@gmod/shared-read-cache';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* in this JS context — so one per RPC worker, plus one on the main
|
|
5
|
-
* which is the scope that actually runs out of memory.
|
|
3
|
+
* The ceiling on what the indexed-format libraries retain, shared by every
|
|
4
|
+
* adapter in this JS context — so one per RPC worker, plus one on the main
|
|
5
|
+
* thread, which is the scope that actually runs out of memory.
|
|
6
6
|
*
|
|
7
|
-
* ## Why
|
|
7
|
+
* ## Why this is not per file
|
|
8
8
|
*
|
|
9
9
|
* `@gmod/bam`, `@gmod/tabix` and `@gmod/cram` each take a per-file budget,
|
|
10
10
|
* sized so that a single track panning never falls off the cliff where a cache
|
|
@@ -29,24 +29,27 @@ import { SharedBudget } from '@gmod/shared-read-cache';
|
|
|
29
29
|
* at hand their space to the one being panned, so the active track keeps a
|
|
30
30
|
* whole working set however many are open.
|
|
31
31
|
*
|
|
32
|
-
* ## The
|
|
32
|
+
* ## The number is not new
|
|
33
33
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
34
|
+
* 1 GB is the libraries' own per-file default (`DEFAULT_MAX_CACHE_BYTES` in
|
|
35
|
+
* both `@gmod/bam` and `@gmod/cram`), moved from per-file to per-context. That
|
|
36
|
+
* makes this a strict tightening — retention can only fall — and avoids
|
|
37
|
+
* inventing a number that would need its own justification. With one file open
|
|
38
|
+
* the two ceilings coincide; from the second file on, this one is the one that
|
|
39
|
+
* binds. The budget still has to clear one query's working set or the cliff
|
|
40
|
+
* returns, and on that workload 512 MB shared across three tracks already cost
|
|
41
|
+
* 43 refills where 1 GB cost 4.
|
|
40
42
|
*
|
|
41
|
-
* ##
|
|
43
|
+
* ## One budget, because every member weighs bytes
|
|
42
44
|
*
|
|
43
45
|
* `SharedBudget.total` is a sum over its members, so they must all weigh in the
|
|
44
|
-
* same unit
|
|
45
|
-
* `@gmod/
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
* same unit, and `sizeOf` is opaque, so nothing could catch a mismatch at
|
|
47
|
+
* runtime. `@gmod/bam` and `@gmod/tabix` weigh decompressed bytes, and since 14
|
|
48
|
+
* `@gmod/cram` weighs a decoded slice by `DecodedSlice.byteLength` — its typed
|
|
49
|
+
* columns exactly and its strings by estimate, within 1–8% of the measured heap
|
|
50
|
+
* (its ADR 0013). Before that a CRAM slice weighed its record count, which is
|
|
51
|
+
* why this used to be two budgets named for their units: a million records
|
|
52
|
+
* could not be added to bytes, and never bound long-read data either, where a
|
|
53
|
+
* 6 MB slice counted as 37.
|
|
49
54
|
*/
|
|
50
55
|
export const decompressedBytesBudget = new SharedBudget(1024 * 2 ** 20);
|
|
51
|
-
/** See {@link decompressedBytesBudget}. Records, not bytes — CRAM only. */
|
|
52
|
-
export const decodedRecordsBudget = new SharedBudget(1_000_000);
|
|
@@ -110,6 +110,7 @@ export interface ActiveFetch {
|
|
|
110
110
|
* passes a {@link createStatusChannel}, which is one volatile instead of two
|
|
111
111
|
* fields and an action.
|
|
112
112
|
*/
|
|
113
|
+
export type StopTokenRotation = ReturnType<typeof createStopTokenRotation>;
|
|
113
114
|
export declare function createStopTokenRotation(self: IStateTreeNode, report: StatusReporter): {
|
|
114
115
|
begin(): ActiveFetch;
|
|
115
116
|
cancel: () => void;
|
|
@@ -28,33 +28,6 @@ export function createStatusChannel() {
|
|
|
28
28
|
},
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Latest-wins stop-token rotation for a fetch that runs in a bare `autorun`
|
|
33
|
-
* rather than through `FetchMixin.runFetch` (which welds the same mechanics to
|
|
34
|
-
* `fetchGeneration`, so it's only for viewport-driven LGV fetches). Each
|
|
35
|
-
* `begin()` aborts the prior fetch's token and returns a fresh one plus an
|
|
36
|
-
* `isCurrent()` guard that captures this run's token — gate every post-await
|
|
37
|
-
* write on it and a superseded or torn-down fetch can never clobber fresher
|
|
38
|
-
* data. The guard is the return value, so a caller can't forget to compare a
|
|
39
|
-
* token by hand.
|
|
40
|
-
*
|
|
41
|
-
* `end()` in the run's `finally` is the other half, and for the same reason —
|
|
42
|
-
* see {@link ActiveFetch.end} for the two things it does together.
|
|
43
|
-
*
|
|
44
|
-
* Owns the token mechanics and the status channel; the caller keeps its own
|
|
45
|
-
* loading/error/commit side-effects. Three holders: `FetchMixin` as a member
|
|
46
|
-
* (so `cancelFetch` can reach it), `installFetch` one per installation (every
|
|
47
|
-
* other fetch in the tree runs on that skeleton), and the synteny diagonalize
|
|
48
|
-
* progress wrapper (`withDiagonalizeProgress`) directly.
|
|
49
|
-
*
|
|
50
|
-
* `report` is passed rather than read off `self`, so where the status lands is
|
|
51
|
-
* the caller's decision and not a shape this imposes. A display passes itself —
|
|
52
|
-
* its status fields are part of its own API, and one composing `FetchMixin`
|
|
53
|
-
* passes the model-wide window along with them (see `StatusReporter`).
|
|
54
|
-
* Anything with one operation to narrate and no status vocabulary of its own
|
|
55
|
-
* passes a {@link createStatusChannel}, which is one volatile instead of two
|
|
56
|
-
* fields and an action.
|
|
57
|
-
*/
|
|
58
31
|
export function createStopTokenRotation(self, report) {
|
|
59
32
|
let currentStopToken;
|
|
60
33
|
// The host's window when it has one, so a display composing `FetchMixin` has
|
|
@@ -124,6 +97,13 @@ export function createStopTokenRotation(self, report) {
|
|
|
124
97
|
statusCallback: stream.statusCallback,
|
|
125
98
|
end() {
|
|
126
99
|
ended = true;
|
|
100
|
+
// A completed fetch's token is released here, not only on supersede
|
|
101
|
+
// or cancel — otherwise it pins its AbortSignal controllers (and blob
|
|
102
|
+
// URL) until the next `begin()`, forever for a one-shot fetch that
|
|
103
|
+
// succeeds and never reruns. `stopStopToken` is guarded, so the
|
|
104
|
+
// superseded and cancelled runs that arrive here already-stopped cost
|
|
105
|
+
// nothing.
|
|
106
|
+
stopStopToken(stopToken);
|
|
127
107
|
if (openStream === stream) {
|
|
128
108
|
openStream = undefined;
|
|
129
109
|
}
|
|
@@ -39,8 +39,8 @@ export interface FetchContext {
|
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Build a {@link FetchContext} over a live fetch's primitives. One
|
|
42
|
-
* constructor, so every context — `runFetch`'s
|
|
43
|
-
* carries the same `callRpc` envelope.
|
|
42
|
+
* constructor, so every context — `FetchMixin.runFetch`'s and `runFetchOnce`'s
|
|
43
|
+
* alike — carries the same `callRpc` envelope.
|
|
44
44
|
*
|
|
45
45
|
* **`self` has to be somewhere `getRpcSessionId` can find one, which in practice
|
|
46
46
|
* means a track or something under it.** `rpcSessionId` is declared by
|
package/esm/util/fetchContext.js
CHANGED
|
@@ -3,8 +3,8 @@ import { createStatusFanOut } from './progress.js';
|
|
|
3
3
|
import { getRpcHost } from './sessionServices.js';
|
|
4
4
|
/**
|
|
5
5
|
* Build a {@link FetchContext} over a live fetch's primitives. One
|
|
6
|
-
* constructor, so every context — `runFetch`'s
|
|
7
|
-
* carries the same `callRpc` envelope.
|
|
6
|
+
* constructor, so every context — `FetchMixin.runFetch`'s and `runFetchOnce`'s
|
|
7
|
+
* alike — carries the same `callRpc` envelope.
|
|
8
8
|
*
|
|
9
9
|
* **`self` has to be somewhere `getRpcSessionId` can find one, which in practice
|
|
10
10
|
* means a track or something under it.** `rpcSessionId` is declared by
|