@jbrowse/product-core 4.2.1 → 5.0.0-beta.1
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 +138 -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 +53 -0
- package/esm/RootModel/PreferencesConfig.js +61 -0
- package/esm/RootModel/createConfigModel.d.ts +232 -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 +310 -11
- package/esm/Session/BaseSession.js +448 -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 +1266 -1128
- package/esm/Session/MultipleViews.js +357 -66
- package/esm/Session/Preferences.d.ts +1665 -0
- package/esm/Session/Preferences.js +54 -0
- package/esm/Session/ReferenceManagement.d.ts +28 -3
- package/esm/Session/ReferenceManagement.js +56 -41
- package/esm/Session/SessionTracks.d.ts +1275 -1138
- package/esm/Session/SessionTracks.js +532 -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 +1169 -1117
- package/esm/Session/Tracks.js +160 -20
- package/esm/Session/assertSessionModel.d.ts +21 -0
- package/esm/Session/assertSessionModel.js +1 -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/shareableSnapshot.d.ts +77 -0
- package/esm/Session/shareableSnapshot.js +196 -0
- package/esm/Session/temporaryAssemblyTracks.d.ts +23 -0
- package/esm/Session/temporaryAssemblyTracks.js +38 -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 +77 -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 +38 -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 +96 -15
- 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 +13 -0
- package/esm/useEngineLifecycle.js +22 -0
- package/package.json +11 -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
|
@@ -1,68 +1,502 @@
|
|
|
1
|
+
import { getConf } from '@jbrowse/core/configuration';
|
|
2
|
+
import SnackbarModel from '@jbrowse/core/ui/SnackbarModel';
|
|
3
|
+
import { setNumberGrouping } from '@jbrowse/core/util';
|
|
4
|
+
import { freezeDeep } from '@jbrowse/core/util/freezeDeep';
|
|
5
|
+
import { isFeature, unwrapFeature } from '@jbrowse/core/util/simpleFeature';
|
|
1
6
|
import { ElementId } from '@jbrowse/core/util/types/mst';
|
|
2
7
|
import { getParent, isStateTreeNode, types } from '@jbrowse/mobx-state-tree';
|
|
8
|
+
import { observable } from 'mobx';
|
|
9
|
+
function isAnimationMode(val) {
|
|
10
|
+
return val === 'system' || val === 'enabled' || val === 'disabled';
|
|
11
|
+
}
|
|
12
|
+
// Promoted per-display-type slot defaults live flat in `preferencesOverrides`
|
|
13
|
+
// under one composite key each (`displayTypeDefault\0<type>\0<slot>`), not under
|
|
14
|
+
// a single nested `displayTypeDefaults` object. Flat keys make each promoted
|
|
15
|
+
// default its own tracked observable-map entry — promoting one can't invalidate
|
|
16
|
+
// a reader of another (every promotable display's `rpcProps` reads one via
|
|
17
|
+
// `getDisplayTypeDefault`) — and collapse the get/set/diff logic to one lookup.
|
|
18
|
+
// The `\0` delimiter can't appear in a display type or slot name.
|
|
19
|
+
// The literal is a **persisted localStorage key prefix**, so treat it as data
|
|
20
|
+
// rather than as an identifier a rename should sweep — a blanket
|
|
21
|
+
// `displayTypeDefault` → `pin` pass did sweep it, and orphans every promoted
|
|
22
|
+
// default already in a browser's storage. Nothing released stores these yet, so
|
|
23
|
+
// the reason it keeps this spelling is naming, not compatibility: the key names
|
|
24
|
+
// *what is stored* (a display-type default, reached through
|
|
25
|
+
// `get/setDisplayTypeDefault`), while `Pin` names the menu control the write
|
|
26
|
+
// is offered from.
|
|
27
|
+
const DISPLAY_TYPE_DEFAULT_PREFIX = 'displayTypeDefault\0';
|
|
28
|
+
// Module-private on purpose: the composite-key spelling is a storage detail of
|
|
29
|
+
// this file's get/set/diff methods, and nothing outside it should be able to
|
|
30
|
+
// depend on the layout.
|
|
31
|
+
function displayTypeDefaultKey(displayType, slot) {
|
|
32
|
+
return `${DISPLAY_TYPE_DEFAULT_PREFIX}${displayType}\0${slot}`;
|
|
33
|
+
}
|
|
34
|
+
function parseDisplayTypeDefaultKey(key) {
|
|
35
|
+
const rest = key.startsWith(DISPLAY_TYPE_DEFAULT_PREFIX)
|
|
36
|
+
? key.slice(DISPLAY_TYPE_DEFAULT_PREFIX.length)
|
|
37
|
+
: '';
|
|
38
|
+
const [displayType, slot] = rest.split('\0');
|
|
39
|
+
return displayType && slot ? { displayType, slot } : undefined;
|
|
40
|
+
}
|
|
41
|
+
// Head of the display path a promoted default takes in a `getPreferenceChanges`
|
|
42
|
+
// row: `[DISPLAY_TYPE_DEFAULTS_PATH_HEAD, displayType, slot]`. Purely a
|
|
43
|
+
// readable row label, deliberately distinct from the flat
|
|
44
|
+
// `DISPLAY_TYPE_DEFAULT_PREFIX` storage key above.
|
|
45
|
+
//
|
|
46
|
+
// Module-private, like the storage key: both the producer
|
|
47
|
+
// (`getPreferenceChanges`) and the consumer (`resetPreferenceChange`) are
|
|
48
|
+
// methods on this model, so the two path shapes this file emits are the only
|
|
49
|
+
// ones it has to undo. It used to be exported for the Preferences dialog to
|
|
50
|
+
// match on and re-route back through `setDisplayTypeDefault`, which meant a
|
|
51
|
+
// rename on one side alone would silently no-op that dialog's per-row reset.
|
|
52
|
+
const DISPLAY_TYPE_DEFAULTS_PATH_HEAD = 'displayTypeDefaults';
|
|
53
|
+
/**
|
|
54
|
+
* #stateModel BaseSessionModel
|
|
55
|
+
*
|
|
56
|
+
* base session shared by all JBrowse products. Be careful what you include
|
|
57
|
+
* here, everything will use it.
|
|
58
|
+
*/
|
|
3
59
|
export function BaseSessionModel(_pluginManager) {
|
|
4
|
-
|
|
60
|
+
const baseModel = types
|
|
5
61
|
.model({
|
|
62
|
+
/**
|
|
63
|
+
* #property
|
|
64
|
+
*/
|
|
6
65
|
id: ElementId,
|
|
66
|
+
/**
|
|
67
|
+
* #property
|
|
68
|
+
*/
|
|
7
69
|
name: types.string,
|
|
8
|
-
|
|
70
|
+
/**
|
|
71
|
+
* #property
|
|
72
|
+
* used to keep track of which view is in focus
|
|
73
|
+
*/
|
|
74
|
+
focusedViewId: types.maybe(types.string),
|
|
75
|
+
/**
|
|
76
|
+
* #property
|
|
77
|
+
* one session-wide toggle for all region highlight bands (URL/view
|
|
78
|
+
* highlights and bookmark overlays)
|
|
79
|
+
*/
|
|
80
|
+
highlightsVisible: types.stripDefault(types.boolean, true),
|
|
81
|
+
/**
|
|
82
|
+
* #property
|
|
83
|
+
* views, tracks, displays and widgets `pruneUnbuildableNodes` took out of
|
|
84
|
+
* the tree because this build has no plugin for their type, each with the
|
|
85
|
+
* anchor that puts it back. Opaque here on purpose — nothing in the
|
|
86
|
+
* session reads it, and the shape belongs to that module.
|
|
87
|
+
*
|
|
88
|
+
* It is a **declared property** rather than data riding along in the
|
|
89
|
+
* snapshot because MST silently drops an undeclared key: without this
|
|
90
|
+
* line the held nodes survive the prune and then vanish the moment
|
|
91
|
+
* `setSession` builds the tree, and every unit test of the prune still
|
|
92
|
+
* passes. `heldNodesSurviveTheSession.test.ts` is the canary.
|
|
93
|
+
*/
|
|
94
|
+
heldForMissingPlugins: types.stripDefault(types.frozen(), undefined),
|
|
9
95
|
})
|
|
10
96
|
.volatile(() => ({
|
|
97
|
+
/**
|
|
98
|
+
* #volatile
|
|
99
|
+
* this is the globally "selected" object. can be anything. code that
|
|
100
|
+
* wants to deal with this should examine it to see what kind of thing it
|
|
101
|
+
* is.
|
|
102
|
+
*/
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
11
104
|
selection: undefined,
|
|
105
|
+
/**
|
|
106
|
+
* #volatile
|
|
107
|
+
* this is the globally "hovered" object. can be anything. code that
|
|
108
|
+
* wants to deal with this should examine it to see what kind of thing it
|
|
109
|
+
* is.
|
|
110
|
+
*/
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
12
112
|
hovered: undefined,
|
|
113
|
+
/**
|
|
114
|
+
* #volatile
|
|
115
|
+
*/
|
|
116
|
+
queueOfDialogs: [],
|
|
117
|
+
/**
|
|
118
|
+
* #volatile
|
|
119
|
+
* runtime user-preference overrides keyed by preference id, resolved by
|
|
120
|
+
* `getPreference` against the `configuration.preferences` admin defaults.
|
|
121
|
+
* Empty here (config-only); products that let users edit preferences load
|
|
122
|
+
* and persist these via localStorage. A runtime override map layered over
|
|
123
|
+
* config defaults, kept off the snapshot since prefs are local UI.
|
|
124
|
+
*
|
|
125
|
+
* An `observable.map` (not a plain object reassigned wholesale) so each
|
|
126
|
+
* preference is its own tracked key: writing one (`setScrollZoom`) can't
|
|
127
|
+
* invalidate a reader of another (`getDisplayTypeDefault` in a track's
|
|
128
|
+
* `rpcProps`). A single spread-replaced object made every setter wake
|
|
129
|
+
* every reader, so toggling scroll-to-zoom re-fetched every track. For the
|
|
130
|
+
* same reason each promoted per-display-type default is a flat composite
|
|
131
|
+
* key (see `displayTypeDefaultKey`), not a single nested `displayTypeDefaults`
|
|
132
|
+
* object — promoting one default can't wake readers of a different one.
|
|
133
|
+
*
|
|
134
|
+
* `deep: false` is load-bearing, not a micro-optimization. The default
|
|
135
|
+
* enhancer wraps an object/array value in a MobX Proxy on `set`, and a
|
|
136
|
+
* promoted default is handed straight back out by `getConf` — so an
|
|
137
|
+
* object-valued promotable slot (alignments `colorBy`) put a Proxy into
|
|
138
|
+
* `rpcProps()`, and V8's structured-clone serializer rejects a Proxy:
|
|
139
|
+
* `worker.postMessage` threw `DataCloneError` on the next fetch of any
|
|
140
|
+
* track following that default (electron IPC and `structuredClone` in the
|
|
141
|
+
* share bake likewise). The map still notifies per key on `set`, so shallow
|
|
142
|
+
* values lose no reactivity — and nothing can mutate a preference in place,
|
|
143
|
+
* because `setPreferenceOverride` freezes what it stores.
|
|
144
|
+
*/
|
|
145
|
+
preferencesOverrides: observable.map(undefined, {
|
|
146
|
+
deep: false,
|
|
147
|
+
}),
|
|
13
148
|
}))
|
|
14
149
|
.views(self => ({
|
|
150
|
+
/**
|
|
151
|
+
* #getter
|
|
152
|
+
*/
|
|
15
153
|
get root() {
|
|
16
154
|
return getParent(self);
|
|
17
155
|
},
|
|
18
156
|
}))
|
|
19
157
|
.views(self => ({
|
|
158
|
+
/**
|
|
159
|
+
* #getter
|
|
160
|
+
*/
|
|
20
161
|
get jbrowse() {
|
|
21
162
|
return self.root.jbrowse;
|
|
22
163
|
},
|
|
164
|
+
/**
|
|
165
|
+
* #getter
|
|
166
|
+
*/
|
|
23
167
|
get rpcManager() {
|
|
24
168
|
return self.root.rpcManager;
|
|
25
169
|
},
|
|
170
|
+
/**
|
|
171
|
+
* #getter
|
|
172
|
+
*/
|
|
26
173
|
get configuration() {
|
|
27
174
|
return this.jbrowse.configuration;
|
|
28
175
|
},
|
|
176
|
+
/**
|
|
177
|
+
* #getter
|
|
178
|
+
*/
|
|
29
179
|
get adminMode() {
|
|
30
180
|
return self.root.adminMode;
|
|
31
181
|
},
|
|
182
|
+
/**
|
|
183
|
+
* #getter
|
|
184
|
+
*/
|
|
32
185
|
get textSearchManager() {
|
|
33
186
|
return self.root.textSearchManager;
|
|
34
187
|
},
|
|
35
188
|
}))
|
|
36
189
|
.views(self => ({
|
|
190
|
+
/**
|
|
191
|
+
* #getter
|
|
192
|
+
*/
|
|
37
193
|
get assemblies() {
|
|
38
194
|
return self.jbrowse.assemblies;
|
|
39
195
|
},
|
|
196
|
+
/**
|
|
197
|
+
* #getter
|
|
198
|
+
*/
|
|
199
|
+
get DialogComponent() {
|
|
200
|
+
return self.queueOfDialogs[0]?.[0];
|
|
201
|
+
},
|
|
202
|
+
/**
|
|
203
|
+
* #getter
|
|
204
|
+
*/
|
|
205
|
+
get DialogProps() {
|
|
206
|
+
return self.queueOfDialogs[0]?.[1];
|
|
207
|
+
},
|
|
208
|
+
}))
|
|
209
|
+
.views(self => ({
|
|
210
|
+
/**
|
|
211
|
+
* #method
|
|
212
|
+
* the admin/embedder `configuration.preferences` value for a key, ignoring
|
|
213
|
+
* any runtime override — i.e. what a reset falls back to. Exposed rather
|
|
214
|
+
* than inlined because "differs from the default" is a question the
|
|
215
|
+
* Preferences reset diff asks about settings this map doesn't hold (see
|
|
216
|
+
* `defaultUseWorkspaces`).
|
|
217
|
+
*/
|
|
218
|
+
getPreferenceDefault(key) {
|
|
219
|
+
return getConf(self, ['preferences', key]);
|
|
220
|
+
},
|
|
221
|
+
/**
|
|
222
|
+
* #method
|
|
223
|
+
* resolved value of a user preference: a runtime override if the user set
|
|
224
|
+
* one, otherwise the admin/embedder `configuration.preferences` default.
|
|
225
|
+
* The override map is empty unless the product loads it (web/desktop).
|
|
226
|
+
*/
|
|
227
|
+
getPreference(key) {
|
|
228
|
+
const override = self.preferencesOverrides.get(key);
|
|
229
|
+
return override === undefined
|
|
230
|
+
? this.getPreferenceDefault(key)
|
|
231
|
+
: override;
|
|
232
|
+
},
|
|
233
|
+
/**
|
|
234
|
+
* #method
|
|
235
|
+
* resolved value of a per-display-type slot default the user promoted
|
|
236
|
+
* (see `setDisplayTypeDefault`); undefined when nothing was promoted.
|
|
237
|
+
*/
|
|
238
|
+
getDisplayTypeDefault(displayType, slot) {
|
|
239
|
+
return self.preferencesOverrides.get(displayTypeDefaultKey(displayType, slot));
|
|
240
|
+
},
|
|
241
|
+
/**
|
|
242
|
+
* #method
|
|
243
|
+
* every per-display-type default the user has promoted, as
|
|
244
|
+
* `{ displayType, slot, value }` — the inventory the Preferences dialog
|
|
245
|
+
* lists and clears one at a time (`setDisplayTypeDefault(…, undefined)`).
|
|
246
|
+
*
|
|
247
|
+
* Here rather than filtered out of `getPreferenceChanges` by the dialog,
|
|
248
|
+
* because the composite-key layout is this file's: a consumer that
|
|
249
|
+
* recognized these rows by matching the path head is exactly the coupling
|
|
250
|
+
* `DISPLAY_TYPE_DEFAULTS_PATH_HEAD` stopped being exported over, where a
|
|
251
|
+
* rename on one side alone silently no-ops the other.
|
|
252
|
+
*/
|
|
253
|
+
getDisplayTypeDefaults() {
|
|
254
|
+
const found = [];
|
|
255
|
+
for (const [key, value] of self.preferencesOverrides.entries()) {
|
|
256
|
+
const parsed = parseDisplayTypeDefaultKey(key);
|
|
257
|
+
if (parsed) {
|
|
258
|
+
found.push({ ...parsed, value });
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return found;
|
|
262
|
+
},
|
|
263
|
+
/**
|
|
264
|
+
* #method
|
|
265
|
+
* every runtime preference-override that currently differs from its
|
|
266
|
+
* config/admin default, as `{ path, from, to }` rows — the exact set
|
|
267
|
+
* `clearPreferenceOverrides` reverts. Backs the confirmation diff shown
|
|
268
|
+
* before "Reset to defaults" (mirrors the per-track changes dialog). A
|
|
269
|
+
* scalar pref (animationMode, scrollZoom) whose override equals the
|
|
270
|
+
* default is omitted (reverting it is a no-op); each promoted
|
|
271
|
+
* per-display-type default is always a difference from the un-promoted
|
|
272
|
+
* state, so `from` reads "(default)".
|
|
273
|
+
*/
|
|
274
|
+
getPreferenceChanges() {
|
|
275
|
+
const changes = [];
|
|
276
|
+
for (const [key, value] of self.preferencesOverrides.entries()) {
|
|
277
|
+
const dtd = parseDisplayTypeDefaultKey(key);
|
|
278
|
+
if (dtd) {
|
|
279
|
+
changes.push({
|
|
280
|
+
path: [
|
|
281
|
+
DISPLAY_TYPE_DEFAULTS_PATH_HEAD,
|
|
282
|
+
dtd.displayType,
|
|
283
|
+
dtd.slot,
|
|
284
|
+
],
|
|
285
|
+
from: undefined,
|
|
286
|
+
to: value,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
const dflt = this.getPreferenceDefault(key);
|
|
291
|
+
if (value !== dflt) {
|
|
292
|
+
changes.push({
|
|
293
|
+
path: [key],
|
|
294
|
+
from: dflt,
|
|
295
|
+
to: value,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return changes;
|
|
301
|
+
},
|
|
302
|
+
}))
|
|
303
|
+
.views(self => ({
|
|
304
|
+
/**
|
|
305
|
+
* #getter
|
|
306
|
+
* resolved feature-layout animation mode (never undefined)
|
|
307
|
+
*/
|
|
308
|
+
get animationMode() {
|
|
309
|
+
const mode = self.getPreference('animationMode');
|
|
310
|
+
return isAnimationMode(mode) ? mode : 'enabled';
|
|
311
|
+
},
|
|
312
|
+
/**
|
|
313
|
+
* #getter
|
|
314
|
+
* resolved scroll-to-zoom preference. Global and personal (never shared in
|
|
315
|
+
* a session snapshot); every wheel-zoom view reads this single value.
|
|
316
|
+
*/
|
|
317
|
+
get scrollZoom() {
|
|
318
|
+
return self.getPreference('scrollZoom') === true;
|
|
319
|
+
},
|
|
320
|
+
/**
|
|
321
|
+
* #getter
|
|
322
|
+
* resolved thousand-separator preference. Read for display in the
|
|
323
|
+
* Preferences dialog; the formatter itself reads a plain module variable
|
|
324
|
+
* set at startup in each realm (see `setNumberGrouping`), because worker-
|
|
325
|
+
* built strings can't see a main-thread observable.
|
|
326
|
+
*/
|
|
327
|
+
get numberGrouping() {
|
|
328
|
+
return self.getPreference('numberGrouping') !== false;
|
|
329
|
+
},
|
|
40
330
|
}))
|
|
41
331
|
.actions(self => ({
|
|
332
|
+
afterAttach() {
|
|
333
|
+
// push the admin/embedder default down to the formatter, which is a
|
|
334
|
+
// plain module variable rather than an observable (see
|
|
335
|
+
// `setNumberGrouping`). Embedded products stop here; web and desktop
|
|
336
|
+
// compose PreferencesSessionMixin, whose own afterAttach runs after
|
|
337
|
+
// this one and re-applies the value once the user's stored overrides
|
|
338
|
+
// are loaded, so a user override still wins over the config default.
|
|
339
|
+
setNumberGrouping(self.numberGrouping);
|
|
340
|
+
},
|
|
341
|
+
/**
|
|
342
|
+
* #action
|
|
343
|
+
* set the global selection, i.e. the globally-selected object. can be a
|
|
344
|
+
* feature, a view, just about anything
|
|
345
|
+
*
|
|
346
|
+
* A feature is unwrapped on the way in, so app state never holds a
|
|
347
|
+
* jexlFeatureProxy. `isFeature` accepts a proxy, but on one `id` is a
|
|
348
|
+
* data field rather than the method the Feature type promises — every
|
|
349
|
+
* consumer doing `isFeature(selection) ? selection.id() : …` would throw.
|
|
350
|
+
*/
|
|
42
351
|
setSelection(thing) {
|
|
43
|
-
self.selection = thing;
|
|
352
|
+
self.selection = isFeature(thing) ? unwrapFeature(thing) : thing;
|
|
44
353
|
},
|
|
354
|
+
/**
|
|
355
|
+
* #action
|
|
356
|
+
* clears the global selection
|
|
357
|
+
*/
|
|
45
358
|
clearSelection() {
|
|
46
359
|
self.selection = undefined;
|
|
47
360
|
},
|
|
361
|
+
/**
|
|
362
|
+
* #action
|
|
363
|
+
*/
|
|
48
364
|
setHovered(thing) {
|
|
49
365
|
self.hovered = thing;
|
|
50
366
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
367
|
+
/**
|
|
368
|
+
* #action
|
|
369
|
+
* toggle all region highlight bands across every view
|
|
370
|
+
*/
|
|
371
|
+
setHighlightsVisible(arg) {
|
|
372
|
+
self.highlightsVisible = arg;
|
|
373
|
+
},
|
|
374
|
+
/**
|
|
375
|
+
* #action
|
|
376
|
+
* turn highlight bands back on, so a newly made highlight or bookmark is
|
|
377
|
+
* never silently swallowed by an earlier "highlights off"
|
|
378
|
+
*/
|
|
379
|
+
revealHighlights() {
|
|
380
|
+
self.highlightsVisible = true;
|
|
381
|
+
},
|
|
382
|
+
/**
|
|
383
|
+
* #action
|
|
384
|
+
* set a runtime user-preference override (see `getPreference`). Mutates
|
|
385
|
+
* volatile state; products persist these to localStorage. An `undefined`
|
|
386
|
+
* value deletes the key (rather than leaving a phantom entry that
|
|
387
|
+
* `getPreference` reads as absent) so the store never holds dead keys.
|
|
388
|
+
*/
|
|
389
|
+
setPreferenceOverride(key, value) {
|
|
390
|
+
if (value === undefined) {
|
|
391
|
+
self.preferencesOverrides.delete(key);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// frozen because `deep: false` hands an object-valued preference (a
|
|
395
|
+
// promoted `colorBy`) straight back out by reference to every display
|
|
396
|
+
// following it, so the "nothing mutates a preference in place" this
|
|
397
|
+
// store relies on is enforced rather than assumed
|
|
398
|
+
self.preferencesOverrides.set(key, freezeDeep(value));
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
/**
|
|
402
|
+
* #action
|
|
403
|
+
* clear every runtime preference override at once — scrollZoom,
|
|
404
|
+
* animationMode, and every promoted per-display-type default (see
|
|
405
|
+
* `setDisplayTypeDefault`) — so each falls back to its config/admin
|
|
406
|
+
* default. Backs the Preferences dialog "Reset to defaults" button.
|
|
407
|
+
*/
|
|
408
|
+
clearPreferenceOverrides() {
|
|
409
|
+
self.preferencesOverrides.clear();
|
|
410
|
+
},
|
|
411
|
+
/**
|
|
412
|
+
* #action
|
|
413
|
+
* clear a single runtime preference override (see `getPreference`) so it
|
|
414
|
+
* falls back to its config/admin default. Backs the per-entry reset in the
|
|
415
|
+
* Preferences dialog "Reset to defaults" confirmation.
|
|
416
|
+
*/
|
|
417
|
+
clearPreferenceOverride(key) {
|
|
418
|
+
self.preferencesOverrides.delete(key);
|
|
419
|
+
},
|
|
420
|
+
/**
|
|
421
|
+
* #action
|
|
422
|
+
* revert one row emitted by `getPreferenceChanges`, addressed by its
|
|
423
|
+
* display `path`. Backs the per-entry reset in the Preferences dialog's
|
|
424
|
+
* "Reset to defaults" confirmation.
|
|
425
|
+
*
|
|
426
|
+
* Lives here rather than in that dialog because this model owns both path
|
|
427
|
+
* shapes it has to undo: a promoted per-display-type default, whose row
|
|
428
|
+
* path is a readable label over a flat composite storage key, and every
|
|
429
|
+
* other override, whose path *is* its key. The dialog used to re-derive
|
|
430
|
+
* the first case from an exported path-head constant.
|
|
431
|
+
*/
|
|
432
|
+
resetPreferenceChange(path) {
|
|
433
|
+
const [head, displayType, slot] = path;
|
|
434
|
+
if (head === DISPLAY_TYPE_DEFAULTS_PATH_HEAD && displayType && slot) {
|
|
435
|
+
this.setDisplayTypeDefault(displayType, slot, undefined);
|
|
436
|
+
}
|
|
437
|
+
else if (head) {
|
|
438
|
+
this.clearPreferenceOverride(head);
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
/**
|
|
442
|
+
* #action
|
|
443
|
+
* set the global scroll-to-zoom preference (see the `scrollZoom` getter)
|
|
444
|
+
*/
|
|
445
|
+
setScrollZoom(flag) {
|
|
446
|
+
this.setPreferenceOverride('scrollZoom', flag);
|
|
447
|
+
},
|
|
448
|
+
/**
|
|
449
|
+
* #action
|
|
450
|
+
* promote (or, with `value` undefined, clear) a per-display-type slot
|
|
451
|
+
* default. Just a preference override under one flat composite key (see
|
|
452
|
+
* `displayTypeDefaultKey`), so it persists and independently tracks like
|
|
453
|
+
* any other pref, and clearing deletes only that key.
|
|
454
|
+
*/
|
|
455
|
+
setDisplayTypeDefault(displayType, slot, value) {
|
|
456
|
+
this.setPreferenceOverride(displayTypeDefaultKey(displayType, slot), value);
|
|
457
|
+
},
|
|
458
|
+
/**
|
|
459
|
+
* #action
|
|
460
|
+
*/
|
|
461
|
+
setName(str) {
|
|
462
|
+
self.name = str;
|
|
463
|
+
},
|
|
464
|
+
/**
|
|
465
|
+
* #action
|
|
466
|
+
* `undefined` is "no view is focused", which the property has always been
|
|
467
|
+
* able to hold (`types.maybe`) and this had no way to spell. Nothing
|
|
468
|
+
* cleared it on teardown as a result: a view that was focused when it left
|
|
469
|
+
* the session left its id behind, and since every consumer compares
|
|
470
|
+
* `focusedViewId === view.id`, the id matched nothing, the focus ring
|
|
471
|
+
* vanished with nothing to say why, and the dead id persisted into a saved
|
|
472
|
+
* or shared session. `takeOut` clears it now.
|
|
473
|
+
*/
|
|
474
|
+
setFocusedViewId(viewId) {
|
|
475
|
+
self.focusedViewId = viewId;
|
|
476
|
+
},
|
|
477
|
+
/**
|
|
478
|
+
* #action
|
|
479
|
+
*/
|
|
480
|
+
removeActiveDialog() {
|
|
481
|
+
self.queueOfDialogs = self.queueOfDialogs.slice(1);
|
|
482
|
+
},
|
|
483
|
+
/**
|
|
484
|
+
* #action
|
|
485
|
+
*/
|
|
486
|
+
queueDialog(doneCallback) {
|
|
487
|
+
const [component, props] = doneCallback(() => {
|
|
488
|
+
this.removeActiveDialog();
|
|
489
|
+
});
|
|
490
|
+
self.queueOfDialogs = [...self.queueOfDialogs, [component, props]];
|
|
491
|
+
},
|
|
492
|
+
}));
|
|
493
|
+
return types.compose(baseModel, SnackbarModel());
|
|
62
494
|
}
|
|
495
|
+
/** Type guard for BaseSession */
|
|
63
496
|
export function isBaseSession(thing) {
|
|
64
497
|
return 'id' in thing && 'name' in thing && 'root' in thing;
|
|
65
498
|
}
|
|
499
|
+
/** Type guard for whether a thing is JBrowse session */
|
|
66
500
|
export function isSession(thing) {
|
|
67
501
|
return isStateTreeNode(thing) && isBaseSession(thing);
|
|
68
502
|
}
|
|
@@ -1,21 +1,127 @@
|
|
|
1
1
|
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
-
import type { AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
2
|
+
import type { AnyConfiguration, AnyConfigurationModel } from '@jbrowse/core/configuration';
|
|
3
3
|
import type { BaseConnectionConfigModel } from '@jbrowse/core/pluggableElementTypes/models/baseConnectionConfig';
|
|
4
4
|
import type { IAnyStateTreeNode, Instance } from '@jbrowse/mobx-state-tree';
|
|
5
|
+
/**
|
|
6
|
+
* Persisted config of a connection track the user has opened. Keyed by trackId
|
|
7
|
+
* in `connectionTrackConfigs`. `connectionId` records provenance so the track
|
|
8
|
+
* can be grouped under its connection and re-hydrated on demand.
|
|
9
|
+
*/
|
|
10
|
+
export interface ConnectionTrackConfigEntry {
|
|
11
|
+
connectionId: string;
|
|
12
|
+
config: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* #stateModel ConnectionManagementSessionMixin
|
|
16
|
+
*/
|
|
5
17
|
export declare function ConnectionManagementSessionMixin(pluginManager: PluginManager): import("@jbrowse/mobx-state-tree").IModelType<{
|
|
6
|
-
|
|
18
|
+
/**
|
|
19
|
+
* #property
|
|
20
|
+
*/
|
|
21
|
+
connectionInstances: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IArrayType<import("@jbrowse/mobx-state-tree").IAnyType>, [undefined]>;
|
|
22
|
+
/**
|
|
23
|
+
* #property
|
|
24
|
+
* Persisted configs of connection tracks the user has opened, keyed by
|
|
25
|
+
* trackId. Unlike `connectionInstances` (stripped from snapshots, holds
|
|
26
|
+
* the whole fetched hub), this holds only the tracks in use, so an open
|
|
27
|
+
* connection track resolves synchronously on session load without
|
|
28
|
+
* re-establishing the connection.
|
|
29
|
+
*/
|
|
30
|
+
connectionTrackConfigs: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").IType<Record<string, ConnectionTrackConfigEntry>, Record<string, ConnectionTrackConfigEntry>, Record<string, ConnectionTrackConfigEntry>>, [undefined]>;
|
|
7
31
|
}, {
|
|
32
|
+
/**
|
|
33
|
+
* #getter
|
|
34
|
+
*/
|
|
8
35
|
readonly connections: BaseConnectionConfigModel[];
|
|
9
36
|
} & {
|
|
10
|
-
|
|
11
|
-
|
|
37
|
+
/**
|
|
38
|
+
* #action
|
|
39
|
+
*/
|
|
40
|
+
makeConnection(configuration: AnyConfigurationModel, initialSnapshot?: object): any;
|
|
41
|
+
/**
|
|
42
|
+
* #action
|
|
43
|
+
* Remove a live connection instance. Tolerant of an already-dormant
|
|
44
|
+
* connection (its instance is stripped from the session on reload).
|
|
45
|
+
* Leaves persisted open-track configs alone — the connect() error path
|
|
46
|
+
* calls this and the user's already-open tracks must survive a transient
|
|
47
|
+
* failure. Full removal goes through `deleteConnection`.
|
|
48
|
+
*/
|
|
12
49
|
breakConnection(configuration: AnyConfigurationModel): void;
|
|
50
|
+
/**
|
|
51
|
+
* #action
|
|
52
|
+
* Close every track a connection contributed — the live instance's
|
|
53
|
+
* tracks plus any persisted open-track configs (a dormant connection,
|
|
54
|
+
* never expanded this session, still renders its opened tracks from
|
|
55
|
+
* `connectionTrackConfigs`) — from all views/widgets, drop the live
|
|
56
|
+
* instance, and drop the persisted configs. The session is left as if the
|
|
57
|
+
* connection had never loaded.
|
|
58
|
+
*/
|
|
59
|
+
teardownConnection(configuration: AnyConfigurationModel): void;
|
|
60
|
+
/**
|
|
61
|
+
* #action
|
|
62
|
+
* Fully remove a connection: tear down its tracks and live instance, then
|
|
63
|
+
* delete its config.
|
|
64
|
+
*/
|
|
13
65
|
deleteConnection(configuration: AnyConfigurationModel): any;
|
|
14
|
-
|
|
66
|
+
/**
|
|
67
|
+
* #action
|
|
68
|
+
* Adds to the **config**, not the session: `jbrowse.connections`, which
|
|
69
|
+
* every visitor to this instance loads. jbrowse-web overrides this so a
|
|
70
|
+
* non-admin's connection lands in `sessionConnections` instead — see
|
|
71
|
+
* WebSessionConnectionsMixin, whose `addSessionConnectionConf` is what
|
|
72
|
+
* to call when you mean the session specifically rather than "wherever
|
|
73
|
+
* this user's edits go".
|
|
74
|
+
*
|
|
75
|
+
* Applications without that override (Desktop, the embedded products)
|
|
76
|
+
* have only this one destination, and it is the right one there: their
|
|
77
|
+
* config and session are saved as a single document.
|
|
78
|
+
*/
|
|
79
|
+
addConnectionConf(connectionConf: AnyConfiguration): any;
|
|
80
|
+
/**
|
|
81
|
+
* #action
|
|
82
|
+
*/
|
|
15
83
|
clearConnections(): void;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
84
|
+
/**
|
|
85
|
+
* #action
|
|
86
|
+
* Snapshot a just-opened connection track's config into
|
|
87
|
+
* `connectionTrackConfigs` so it survives session reload. No-op if the
|
|
88
|
+
* track isn't connection-provided or is already captured (edits go
|
|
89
|
+
* through `updateConnectionTrackConfig`).
|
|
90
|
+
*/
|
|
91
|
+
captureConnectionTrack(trackId: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* #action
|
|
94
|
+
* Persist an edit to an opened connection track. The full config is
|
|
95
|
+
* stored (not a delta): the connection's fetched "base" isn't present at
|
|
96
|
+
* load, so only a complete config resolves synchronously.
|
|
97
|
+
*/
|
|
98
|
+
updateConnectionTrackConfig(trackConf: Record<string, unknown> & {
|
|
99
|
+
trackId: string;
|
|
100
|
+
}): void;
|
|
101
|
+
/**
|
|
102
|
+
* #action
|
|
103
|
+
* Upsert one opened connection track's persisted config.
|
|
104
|
+
*/
|
|
105
|
+
setConnectionTrackConfig(trackId: string, connectionId: string, config: Record<string, unknown>): void;
|
|
106
|
+
/**
|
|
107
|
+
* #action
|
|
108
|
+
* Drop a connection track's persisted config once no open view still
|
|
109
|
+
* references it, so the session doesn't accumulate closed tracks.
|
|
110
|
+
*/
|
|
111
|
+
pruneConnectionTrackConfig(trackId: string): void;
|
|
112
|
+
/**
|
|
113
|
+
* #action
|
|
114
|
+
* Lazily establish a single connection by id if it isn't already live —
|
|
115
|
+
* used when its category is expanded in the track selector. Fetches
|
|
116
|
+
* silently (no view launch / success snackbar); already-open tracks keep
|
|
117
|
+
* rendering from `connectionTrackConfigs` meanwhile. Idempotent.
|
|
118
|
+
*/
|
|
119
|
+
hydrateConnection(connectionId: string): void;
|
|
120
|
+
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|
|
121
|
+
/** Session mixin MST type for a session that has connections */
|
|
19
122
|
export type SessionWithConnectionsType = ReturnType<typeof ConnectionManagementSessionMixin>;
|
|
123
|
+
/** Instance of a session that has connections: `connectionInstances`,
|
|
124
|
+
* `makeConnection()`, etc. */
|
|
20
125
|
export type SessionWithConnections = Instance<SessionWithConnectionsType>;
|
|
126
|
+
/** Type guard for SessionWithConnections */
|
|
21
127
|
export declare function isSessionWithConnections(session: IAnyStateTreeNode): session is SessionWithConnections;
|