@jbrowse/product-core 5.0.0-beta.1 → 5.0.0-beta.3
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.js +2 -1
- package/esm/RootModel/PreferencesConfig.d.ts +15 -0
- package/esm/RootModel/PreferencesConfig.js +15 -0
- package/esm/RootModel/createConfigModel.d.ts +8 -0
- package/esm/Session/BaseSession.d.ts +11 -26
- package/esm/Session/BaseSession.js +21 -60
- package/esm/Session/MultipleViews.d.ts +17 -3
- package/esm/Session/MultipleViews.js +45 -5
- package/esm/Session/Preferences.d.ts +2 -1
- package/esm/Session/Preferences.js +5 -0
- package/esm/Session/SessionTracks.d.ts +4 -1
- package/esm/Session/SessionTracks.js +8 -0
- package/esm/Session/TrackMenu.js +2 -2
- package/esm/Session/Tracks.d.ts +4 -5
- package/esm/Session/Tracks.js +115 -92
- package/esm/Session/developerMode.d.ts +7 -0
- package/esm/Session/developerMode.js +43 -0
- package/esm/Session/readdedTrackConf.d.ts +8 -0
- package/esm/Session/readdedTrackConf.js +25 -0
- package/esm/Session/temporaryAssemblyTracks.d.ts +4 -3
- package/esm/Session/temporaryAssemblyTracks.js +6 -7
- package/esm/controllerTracks.d.ts +3 -3
- package/esm/controllerTracks.js +17 -4
- package/esm/index.d.ts +2 -2
- package/esm/index.js +2 -2
- package/esm/loadPlugins.d.ts +1 -1
- package/esm/loadPlugins.js +6 -3
- package/esm/pruneUnbuildableNodes.js +5 -2
- package/esm/rpcWorker.js +5 -4
- package/esm/ui/AboutDialogContents.js +2 -1
- package/esm/ui/DescriptionPanel.d.ts +3 -0
- package/esm/ui/DescriptionPanel.js +24 -0
- package/esm/ui/DisplayDefaultsSection.d.ts +7 -20
- package/esm/ui/DisplayDefaultsSection.js +9 -47
- package/esm/ui/GeneralPreferencesTab.d.ts +14 -0
- package/esm/ui/GeneralPreferencesTab.js +27 -0
- package/esm/ui/PreferenceCheckbox.d.ts +6 -0
- package/esm/ui/PreferenceCheckbox.js +16 -0
- package/esm/ui/PreferencesDialog.d.ts +6 -27
- package/esm/ui/PreferencesDialog.js +38 -119
- package/esm/ui/ViewPreferencesTab.d.ts +12 -0
- package/esm/ui/ViewPreferencesTab.js +15 -0
- package/esm/ui/descriptionHtml.d.ts +26 -0
- package/esm/ui/descriptionHtml.js +66 -0
- package/esm/ui/displayDefaultChanges.d.ts +31 -0
- package/esm/ui/displayDefaultChanges.js +66 -0
- package/esm/ui/preferencesReset.d.ts +18 -0
- package/esm/ui/preferencesReset.js +64 -0
- package/esm/useEngineLifecycle.d.ts +7 -1
- package/esm/useEngineLifecycle.js +24 -2
- package/package.json +5 -4
- package/esm/scheduleDetachedDestroy.d.ts +0 -18
- package/esm/scheduleDetachedDestroy.js +0 -24
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import TextSearchManager from '@jbrowse/core/TextSearch/TextSearchManager';
|
|
2
2
|
import assemblyManagerFactory from '@jbrowse/core/assemblyManager';
|
|
3
3
|
import RpcManager from '@jbrowse/core/rpc/RpcManager';
|
|
4
|
+
import { scheduleDetachedDestroy } from '@jbrowse/core/util';
|
|
4
5
|
import { cast, detach, getType, isStateTreeNode, types, } from '@jbrowse/mobx-state-tree';
|
|
5
6
|
import { describeUnbuildableNodes, pruneUnbuildableNodes, } from '../pruneUnbuildableNodes.js';
|
|
6
|
-
import { scheduleDetachedDestroy } from '../scheduleDetachedDestroy.js';
|
|
7
7
|
import { migrateSessionSnapshot } from '../sessionMigrations/index.js';
|
|
8
8
|
import { filterSessionInPlace } from '../sessionUtils.js';
|
|
9
9
|
// `session` is a `types.maybe(sessionModelType)` where `sessionModelType` is the
|
|
@@ -87,6 +87,7 @@ export function BaseRootModelFactory({ pluginManager, jbrowseModelType, sessionM
|
|
|
87
87
|
const unbuildable = [];
|
|
88
88
|
let migrated = sessionSnapshot;
|
|
89
89
|
if (sessionSnapshot && typeof sessionSnapshot === 'object') {
|
|
90
|
+
pluginManager.assertSessionTypesLoaded(sessionSnapshot);
|
|
90
91
|
// before the cast, because a type this build has no plugin for is a
|
|
91
92
|
// union failure the try below never gets to see
|
|
92
93
|
const pruned = pruneUnbuildableNodes(migrateSessionSnapshot(sessionSnapshot), pluginManager, sessionModelType);
|
|
@@ -50,4 +50,19 @@ export declare function PreferencesConfigSchemaFactory(): import("@jbrowse/core/
|
|
|
50
50
|
readonly type: "boolean";
|
|
51
51
|
readonly defaultValue: false;
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* #slot configuration.preferences.developerMode
|
|
55
|
+
* when true, the ordering-contract checks a display or plugin can break
|
|
56
|
+
* report themselves in the app rather than only in a development build.
|
|
57
|
+
* Off by default and deliberately not in the Preferences dialog: the
|
|
58
|
+
* messages are about code, and a reader who cannot change the code cannot
|
|
59
|
+
* act on one. Turn it on for a site that is running a plugin under
|
|
60
|
+
* development; a plugin author needs nothing here, since a plugin served
|
|
61
|
+
* from localhost arms the same channel on its own, as does
|
|
62
|
+
* `localStorage.jbrowseDeveloperMode`. Takes effect on reload.
|
|
63
|
+
*/
|
|
64
|
+
readonly developerMode: {
|
|
65
|
+
readonly type: "boolean";
|
|
66
|
+
readonly defaultValue: false;
|
|
67
|
+
};
|
|
53
68
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
@@ -57,5 +57,20 @@ export function PreferencesConfigSchemaFactory() {
|
|
|
57
57
|
type: 'boolean',
|
|
58
58
|
defaultValue: false,
|
|
59
59
|
},
|
|
60
|
+
/**
|
|
61
|
+
* #slot configuration.preferences.developerMode
|
|
62
|
+
* when true, the ordering-contract checks a display or plugin can break
|
|
63
|
+
* report themselves in the app rather than only in a development build.
|
|
64
|
+
* Off by default and deliberately not in the Preferences dialog: the
|
|
65
|
+
* messages are about code, and a reader who cannot change the code cannot
|
|
66
|
+
* act on one. Turn it on for a site that is running a plugin under
|
|
67
|
+
* development; a plugin author needs nothing here, since a plugin served
|
|
68
|
+
* from localhost arms the same channel on its own, as does
|
|
69
|
+
* `localStorage.jbrowseDeveloperMode`. Takes effect on reload.
|
|
70
|
+
*/
|
|
71
|
+
developerMode: {
|
|
72
|
+
type: 'boolean',
|
|
73
|
+
defaultValue: false,
|
|
74
|
+
},
|
|
60
75
|
});
|
|
61
76
|
}
|
|
@@ -66,6 +66,10 @@ declare function rootConfigurationSchema(): import("@jbrowse/core/configuration/
|
|
|
66
66
|
readonly type: "boolean";
|
|
67
67
|
readonly defaultValue: false;
|
|
68
68
|
};
|
|
69
|
+
readonly developerMode: {
|
|
70
|
+
readonly type: "boolean";
|
|
71
|
+
readonly defaultValue: false;
|
|
72
|
+
};
|
|
69
73
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
70
74
|
readonly formatDetails: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
71
75
|
readonly feature: {
|
|
@@ -178,6 +182,10 @@ export declare function createConfigModel(pluginManager: PluginManager, assembly
|
|
|
178
182
|
readonly type: "boolean";
|
|
179
183
|
readonly defaultValue: false;
|
|
180
184
|
};
|
|
185
|
+
readonly developerMode: {
|
|
186
|
+
readonly type: "boolean";
|
|
187
|
+
readonly defaultValue: false;
|
|
188
|
+
};
|
|
181
189
|
}, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<undefined, undefined>>;
|
|
182
190
|
readonly formatDetails: import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
|
|
183
191
|
readonly feature: {
|
|
@@ -162,11 +162,8 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
|
|
|
162
162
|
* `{ displayType, slot, value }` — the inventory the Preferences dialog
|
|
163
163
|
* lists and clears one at a time (`setDisplayTypeDefault(…, undefined)`).
|
|
164
164
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* recognized these rows by matching the path head is exactly the coupling
|
|
168
|
-
* `DISPLAY_TYPE_DEFAULTS_PATH_HEAD` stopped being exported over, where a
|
|
169
|
-
* rename on one side alone silently no-ops the other.
|
|
165
|
+
* Parsed here rather than by the dialog, because the composite-key
|
|
166
|
+
* layout is this file's and nothing outside it should depend on it.
|
|
170
167
|
*/
|
|
171
168
|
getDisplayTypeDefaults(): {
|
|
172
169
|
displayType: string;
|
|
@@ -175,14 +172,13 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
|
|
|
175
172
|
}[];
|
|
176
173
|
/**
|
|
177
174
|
* #method
|
|
178
|
-
* every
|
|
179
|
-
* config/admin default, as `{ path, from, to }` rows
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* state, so `from` reads "(default)".
|
|
175
|
+
* every scalar preference override that currently differs from its
|
|
176
|
+
* config/admin default, as `{ path, from, to }` rows whose path is the
|
|
177
|
+
* override's own key. A scalar pref (animationMode, scrollZoom) whose
|
|
178
|
+
* override equals the default is omitted, since reverting it is a no-op.
|
|
179
|
+
* Promoted display-type defaults share the map but are not here: they
|
|
180
|
+
* are `getDisplayTypeDefaults`, and the Preferences dialog lists them
|
|
181
|
+
* through one builder on both of its surfaces.
|
|
186
182
|
*/
|
|
187
183
|
getPreferenceChanges(): TrackConfigChange[];
|
|
188
184
|
} & {
|
|
@@ -261,19 +257,6 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
|
|
|
261
257
|
* Preferences dialog "Reset to defaults" confirmation.
|
|
262
258
|
*/
|
|
263
259
|
clearPreferenceOverride(key: string): void;
|
|
264
|
-
/**
|
|
265
|
-
* #action
|
|
266
|
-
* revert one row emitted by `getPreferenceChanges`, addressed by its
|
|
267
|
-
* display `path`. Backs the per-entry reset in the Preferences dialog's
|
|
268
|
-
* "Reset to defaults" confirmation.
|
|
269
|
-
*
|
|
270
|
-
* Lives here rather than in that dialog because this model owns both path
|
|
271
|
-
* shapes it has to undo: a promoted per-display-type default, whose row
|
|
272
|
-
* path is a readable label over a flat composite storage key, and every
|
|
273
|
-
* other override, whose path *is* its key. The dialog used to re-derive
|
|
274
|
-
* the first case from an exported path-head constant.
|
|
275
|
-
*/
|
|
276
|
-
resetPreferenceChange(path: string[]): void;
|
|
277
260
|
/**
|
|
278
261
|
* #action
|
|
279
262
|
* set the global scroll-to-zoom preference (see the `scrollZoom` getter)
|
|
@@ -322,6 +305,8 @@ export declare function BaseSessionModel<ROOT_MODEL_TYPE extends BaseRootModelTy
|
|
|
322
305
|
pushSnackbarMessage(message: string, level?: import("@jbrowse/core/util").NotificationLevel, actions?: import("@jbrowse/core/util").SnackAction[]): void;
|
|
323
306
|
popSnackbarMessage(): import("@jbrowse/core/ui/SnackbarModel").SnackbarMessage | undefined;
|
|
324
307
|
removeSnackbarMessage(message: string): void;
|
|
308
|
+
} & {
|
|
309
|
+
afterAttach(): void;
|
|
325
310
|
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|
|
326
311
|
/** Session mixin MST type for the most basic session */
|
|
327
312
|
export type BaseSessionType = ReturnType<typeof BaseSessionModel>;
|
|
@@ -6,6 +6,7 @@ import { isFeature, unwrapFeature } from '@jbrowse/core/util/simpleFeature';
|
|
|
6
6
|
import { ElementId } from '@jbrowse/core/util/types/mst';
|
|
7
7
|
import { getParent, isStateTreeNode, types } from '@jbrowse/mobx-state-tree';
|
|
8
8
|
import { observable } from 'mobx';
|
|
9
|
+
import { applyDeveloperMode } from './developerMode.js';
|
|
9
10
|
function isAnimationMode(val) {
|
|
10
11
|
return val === 'system' || val === 'enabled' || val === 'disabled';
|
|
11
12
|
}
|
|
@@ -38,18 +39,6 @@ function parseDisplayTypeDefaultKey(key) {
|
|
|
38
39
|
const [displayType, slot] = rest.split('\0');
|
|
39
40
|
return displayType && slot ? { displayType, slot } : undefined;
|
|
40
41
|
}
|
|
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
42
|
/**
|
|
54
43
|
* #stateModel BaseSessionModel
|
|
55
44
|
*
|
|
@@ -244,11 +233,8 @@ export function BaseSessionModel(_pluginManager) {
|
|
|
244
233
|
* `{ displayType, slot, value }` — the inventory the Preferences dialog
|
|
245
234
|
* lists and clears one at a time (`setDisplayTypeDefault(…, undefined)`).
|
|
246
235
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
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.
|
|
236
|
+
* Parsed here rather than by the dialog, because the composite-key
|
|
237
|
+
* layout is this file's and nothing outside it should depend on it.
|
|
252
238
|
*/
|
|
253
239
|
getDisplayTypeDefaults() {
|
|
254
240
|
const found = [];
|
|
@@ -262,31 +248,18 @@ export function BaseSessionModel(_pluginManager) {
|
|
|
262
248
|
},
|
|
263
249
|
/**
|
|
264
250
|
* #method
|
|
265
|
-
* every
|
|
266
|
-
* config/admin default, as `{ path, from, to }` rows
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* state, so `from` reads "(default)".
|
|
251
|
+
* every scalar preference override that currently differs from its
|
|
252
|
+
* config/admin default, as `{ path, from, to }` rows whose path is the
|
|
253
|
+
* override's own key. A scalar pref (animationMode, scrollZoom) whose
|
|
254
|
+
* override equals the default is omitted, since reverting it is a no-op.
|
|
255
|
+
* Promoted display-type defaults share the map but are not here: they
|
|
256
|
+
* are `getDisplayTypeDefaults`, and the Preferences dialog lists them
|
|
257
|
+
* through one builder on both of its surfaces.
|
|
273
258
|
*/
|
|
274
259
|
getPreferenceChanges() {
|
|
275
260
|
const changes = [];
|
|
276
261
|
for (const [key, value] of self.preferencesOverrides.entries()) {
|
|
277
|
-
|
|
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 {
|
|
262
|
+
if (!key.startsWith(DISPLAY_TYPE_DEFAULT_PREFIX)) {
|
|
290
263
|
const dflt = this.getPreferenceDefault(key);
|
|
291
264
|
if (value !== dflt) {
|
|
292
265
|
changes.push({
|
|
@@ -417,27 +390,6 @@ export function BaseSessionModel(_pluginManager) {
|
|
|
417
390
|
clearPreferenceOverride(key) {
|
|
418
391
|
self.preferencesOverrides.delete(key);
|
|
419
392
|
},
|
|
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
393
|
/**
|
|
442
394
|
* #action
|
|
443
395
|
* set the global scroll-to-zoom preference (see the `scrollZoom` getter)
|
|
@@ -490,7 +442,16 @@ export function BaseSessionModel(_pluginManager) {
|
|
|
490
442
|
self.queueOfDialogs = [...self.queueOfDialogs, [component, props]];
|
|
491
443
|
},
|
|
492
444
|
}));
|
|
493
|
-
return types.compose(baseModel, SnackbarModel())
|
|
445
|
+
return types.compose(baseModel, SnackbarModel()).actions(self => ({
|
|
446
|
+
// after the compose rather than in the afterAttach above, because the
|
|
447
|
+
// notices land in the snackbar this line is the first place to hold both
|
|
448
|
+
// halves of. Web and desktop reach it a second time once the user's stored
|
|
449
|
+
// overrides are loaded (`PreferencesSessionMixin`), and our MST fork
|
|
450
|
+
// auto-chains both hooks.
|
|
451
|
+
afterAttach() {
|
|
452
|
+
applyDeveloperMode(self);
|
|
453
|
+
},
|
|
454
|
+
}));
|
|
494
455
|
}
|
|
495
456
|
/** Type guard for BaseSession */
|
|
496
457
|
export function isBaseSession(thing) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { ViewSnapshotInput } from '@jbrowse/core/PluginManager';
|
|
2
3
|
import type { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes';
|
|
3
4
|
import type { IAnyStateTreeNode, Instance } from '@jbrowse/mobx-state-tree';
|
|
4
5
|
/**
|
|
@@ -1659,7 +1660,6 @@ export declare function MultipleViewsSessionMixin(pluginManager: PluginManager):
|
|
|
1659
1660
|
setPreferenceOverride(key: string, value: unknown): void;
|
|
1660
1661
|
clearPreferenceOverrides(): void;
|
|
1661
1662
|
clearPreferenceOverride(key: string): void;
|
|
1662
|
-
resetPreferenceChange(path: string[]): void;
|
|
1663
1663
|
setScrollZoom(flag: boolean): void;
|
|
1664
1664
|
setDisplayTypeDefault(displayType: string, slot: string, value: unknown): void;
|
|
1665
1665
|
setName(str: string): void;
|
|
@@ -1678,6 +1678,8 @@ export declare function MultipleViewsSessionMixin(pluginManager: PluginManager):
|
|
|
1678
1678
|
pushSnackbarMessage(message: string, level?: import("@jbrowse/core/util").NotificationLevel, actions?: import("@jbrowse/core/util").SnackAction[]): void;
|
|
1679
1679
|
popSnackbarMessage(): import("@jbrowse/core/ui/SnackbarModel").SnackbarMessage | undefined;
|
|
1680
1680
|
removeSnackbarMessage(message: string): void;
|
|
1681
|
+
} & {
|
|
1682
|
+
afterAttach(): void;
|
|
1681
1683
|
} & {
|
|
1682
1684
|
readonly visibleWidget: import("@jbrowse/core/util").Widget | undefined;
|
|
1683
1685
|
} & {
|
|
@@ -1752,8 +1754,10 @@ export declare function MultipleViewsSessionMixin(pluginManager: PluginManager):
|
|
|
1752
1754
|
orderViews(ids: string[]): void;
|
|
1753
1755
|
/**
|
|
1754
1756
|
* #action
|
|
1757
|
+
* Requires the view type's state model to be loaded; for a lazily
|
|
1758
|
+
* registered type use `launchView`.
|
|
1755
1759
|
*/
|
|
1756
|
-
addView(typeName:
|
|
1760
|
+
addView<N extends string>(typeName: N, initialState?: NoInfer<ViewSnapshotInput<N>>): any;
|
|
1757
1761
|
/**
|
|
1758
1762
|
* #action
|
|
1759
1763
|
* swap `view` for a new view of `typeName`, in the slot it occupied.
|
|
@@ -1783,7 +1787,7 @@ export declare function MultipleViewsSessionMixin(pluginManager: PluginManager):
|
|
|
1783
1787
|
* its panel, and the launch that offers a replace is a click on that
|
|
1784
1788
|
* view's own menu.
|
|
1785
1789
|
*/
|
|
1786
|
-
replaceView(view: IBaseViewModel, typeName:
|
|
1790
|
+
replaceView<N extends string>(view: IBaseViewModel, typeName: N, initialState?: NoInfer<ViewSnapshotInput<N>>): any;
|
|
1787
1791
|
/**
|
|
1788
1792
|
* #action
|
|
1789
1793
|
*/
|
|
@@ -1845,6 +1849,16 @@ export declare function MultipleViewsSessionMixin(pluginManager: PluginManager):
|
|
|
1845
1849
|
*/
|
|
1846
1850
|
resetUseWorkspaces(): void;
|
|
1847
1851
|
afterAttach(): void;
|
|
1852
|
+
} & {
|
|
1853
|
+
/**
|
|
1854
|
+
* #action
|
|
1855
|
+
* `addView` for view types whose state model may be lazily loaded. The
|
|
1856
|
+
* view is not in `session.views` until the promise resolves.
|
|
1857
|
+
* `getViewType` first so an unknown type name fails by that name; the
|
|
1858
|
+
* snapshot walk skips unregistered names and covers the displays of the
|
|
1859
|
+
* tracks a launch opens with.
|
|
1860
|
+
*/
|
|
1861
|
+
launchView<N extends string>(typeName: N, initialState?: NoInfer<ViewSnapshotInput<N>>): Promise<any>;
|
|
1848
1862
|
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree").ModelSnapshotType<Omit<Omit<Omit<{
|
|
1849
1863
|
id: import("@jbrowse/mobx-state-tree").IOptionalIType<import("@jbrowse/mobx-state-tree").ISimpleType<string>, [undefined]>;
|
|
1850
1864
|
name: import("@jbrowse/mobx-state-tree").ISimpleType<string>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { applyOrderWithin, localStorageGetBoolean, localStorageSetBoolean, reorderWithin, } from '@jbrowse/core/util';
|
|
1
|
+
import { applyOrderWithin, localStorageGetBoolean, localStorageSetBoolean, reorderWithin, scheduleDetachedDestroy, } from '@jbrowse/core/util';
|
|
2
2
|
import { addDisposer, applyPatch, cast, detach, getMembers, getParent, hasParent, isReferenceType, isStateTreeNode, types, } from '@jbrowse/mobx-state-tree';
|
|
3
3
|
import { autorun } from 'mobx';
|
|
4
|
-
import { scheduleDetachedDestroy } from '../scheduleDetachedDestroy.js';
|
|
5
4
|
import { BaseSessionModel, isBaseSession } from './BaseSession.js';
|
|
6
5
|
import { DrawerWidgetSessionMixin } from './DrawerWidgets.js';
|
|
7
6
|
// Whether `node` IS `view` or lives somewhere under it. A view can CONTAIN
|
|
@@ -54,10 +53,24 @@ function dropRefsInto(holder, view) {
|
|
|
54
53
|
}
|
|
55
54
|
return found;
|
|
56
55
|
}
|
|
56
|
+
// A session snapshot's `views`, or undefined for anything that is not one
|
|
57
|
+
function viewsOfSessionSnapshot(snapshot) {
|
|
58
|
+
const views = typeof snapshot === 'object' && snapshot !== null && 'views' in snapshot
|
|
59
|
+
? snapshot.views
|
|
60
|
+
: undefined;
|
|
61
|
+
return Array.isArray(views) ? views : undefined;
|
|
62
|
+
}
|
|
57
63
|
/**
|
|
58
64
|
* #stateModel MultipleViewsSessionMixin
|
|
59
65
|
*/
|
|
60
66
|
export function MultipleViewsSessionMixin(pluginManager) {
|
|
67
|
+
// an unknown type name passes through to fail at the union as it always has
|
|
68
|
+
function assertViewStateModelLoaded(typeName) {
|
|
69
|
+
if (pluginManager.getElementTypeRecord('view').has(typeName) &&
|
|
70
|
+
!pluginManager.getViewType(typeName).isStateModelLoaded) {
|
|
71
|
+
throw new Error(`state model for view type ${typeName} is not loaded yet — use launchView(), or await getViewType('${typeName}').loadStateModel() first`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
61
74
|
return types
|
|
62
75
|
.compose(BaseSessionModel(pluginManager), DrawerWidgetSessionMixin(pluginManager), types.model({
|
|
63
76
|
/**
|
|
@@ -208,8 +221,11 @@ export function MultipleViewsSessionMixin(pluginManager) {
|
|
|
208
221
|
},
|
|
209
222
|
/**
|
|
210
223
|
* #action
|
|
224
|
+
* Requires the view type's state model to be loaded; for a lazily
|
|
225
|
+
* registered type use `launchView`.
|
|
211
226
|
*/
|
|
212
|
-
addView(typeName, initialState
|
|
227
|
+
addView(typeName, initialState) {
|
|
228
|
+
assertViewStateModelLoaded(typeName);
|
|
213
229
|
const length = self.views.push({
|
|
214
230
|
...initialState,
|
|
215
231
|
type: typeName,
|
|
@@ -245,7 +261,8 @@ export function MultipleViewsSessionMixin(pluginManager) {
|
|
|
245
261
|
* its panel, and the launch that offers a replace is a click on that
|
|
246
262
|
* view's own menu.
|
|
247
263
|
*/
|
|
248
|
-
replaceView(view, typeName, initialState
|
|
264
|
+
replaceView(view, typeName, initialState) {
|
|
265
|
+
assertViewStateModelLoaded(typeName);
|
|
249
266
|
// read before the removal, which is what makes both stale
|
|
250
267
|
const idx = self.views.indexOf(view);
|
|
251
268
|
// `idx` first, so a view already gone from the session short-circuits
|
|
@@ -305,7 +322,13 @@ export function MultipleViewsSessionMixin(pluginManager) {
|
|
|
305
322
|
* lands, which is why ADR-069's rule stops there.
|
|
306
323
|
*/
|
|
307
324
|
takeOutViewsMissingFrom(snapshot) {
|
|
308
|
-
const incoming = snapshot
|
|
325
|
+
const incoming = viewsOfSessionSnapshot(snapshot);
|
|
326
|
+
// an argument that is not a session snapshot used to read as "keep
|
|
327
|
+
// none" and close every view: an agent introspecting the actions
|
|
328
|
+
// called this with a tab id and a keep-list and lost its session
|
|
329
|
+
if (!incoming) {
|
|
330
|
+
throw new Error('takeOutViewsMissingFrom takes a session snapshot (it is what undo and redo call to close the views a snapshot no longer has); to close one view, session.removeView(view)');
|
|
331
|
+
}
|
|
309
332
|
const kept = new Map(incoming.map(v => [v.id, v.type]));
|
|
310
333
|
// a copy, because `takeOut` splices the array it is iterating
|
|
311
334
|
for (const view of [...self.views]) {
|
|
@@ -359,6 +382,23 @@ export function MultipleViewsSessionMixin(pluginManager) {
|
|
|
359
382
|
},
|
|
360
383
|
};
|
|
361
384
|
})
|
|
385
|
+
.actions(self => ({
|
|
386
|
+
/**
|
|
387
|
+
* #action
|
|
388
|
+
* `addView` for view types whose state model may be lazily loaded. The
|
|
389
|
+
* view is not in `session.views` until the promise resolves.
|
|
390
|
+
* `getViewType` first so an unknown type name fails by that name; the
|
|
391
|
+
* snapshot walk skips unregistered names and covers the displays of the
|
|
392
|
+
* tracks a launch opens with.
|
|
393
|
+
*/
|
|
394
|
+
async launchView(typeName, initialState) {
|
|
395
|
+
await pluginManager.getViewType(typeName).loadStateModel();
|
|
396
|
+
await pluginManager.preloadSessionTypes({
|
|
397
|
+
views: [{ type: typeName, ...initialState }],
|
|
398
|
+
});
|
|
399
|
+
return self.addView(typeName, initialState);
|
|
400
|
+
},
|
|
401
|
+
}))
|
|
362
402
|
.postProcessSnapshot(snap => {
|
|
363
403
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
364
404
|
if (!snap) {
|
|
@@ -1641,7 +1641,6 @@ export declare function PreferencesSessionMixin(pluginManager: PluginManager): i
|
|
|
1641
1641
|
setPreferenceOverride(key: string, value: unknown): void;
|
|
1642
1642
|
clearPreferenceOverrides(): void;
|
|
1643
1643
|
clearPreferenceOverride(key: string): void;
|
|
1644
|
-
resetPreferenceChange(path: string[]): void;
|
|
1645
1644
|
setScrollZoom(flag: boolean): void;
|
|
1646
1645
|
setDisplayTypeDefault(displayType: string, slot: string, value: unknown): void;
|
|
1647
1646
|
setName(str: string): void;
|
|
@@ -1662,4 +1661,6 @@ export declare function PreferencesSessionMixin(pluginManager: PluginManager): i
|
|
|
1662
1661
|
removeSnackbarMessage(message: string): void;
|
|
1663
1662
|
} & {
|
|
1664
1663
|
afterAttach(): void;
|
|
1664
|
+
} & {
|
|
1665
|
+
afterAttach(): void;
|
|
1665
1666
|
}, import("@jbrowse/mobx-state-tree")._NotCustomized, import("@jbrowse/mobx-state-tree")._NotCustomized>;
|
|
@@ -3,6 +3,7 @@ import { freezeDeep } from '@jbrowse/core/util/freezeDeep';
|
|
|
3
3
|
import { addDisposer, types } from '@jbrowse/mobx-state-tree';
|
|
4
4
|
import { autorun } from 'mobx';
|
|
5
5
|
import { BaseSessionModel } from './BaseSession.js';
|
|
6
|
+
import { applyDeveloperMode } from './developerMode.js';
|
|
6
7
|
const PREFS_KEY = 'jbrowsePreferences';
|
|
7
8
|
function loadStoredPreferences() {
|
|
8
9
|
const stored = localStorageGetItem(PREFS_KEY);
|
|
@@ -43,6 +44,10 @@ export function PreferencesSessionMixin(pluginManager) {
|
|
|
43
44
|
// main-thread update would leave the app formatted two ways at once.
|
|
44
45
|
// The dialog says a reload is needed; this is where the reload lands.
|
|
45
46
|
setNumberGrouping(self.numberGrouping);
|
|
47
|
+
// and the same second pass for the developer-notice channel, whose
|
|
48
|
+
// admin default BaseSession already read: a user's stored override is
|
|
49
|
+
// only loaded a line above this
|
|
50
|
+
applyDeveloperMode(self);
|
|
46
51
|
addDisposer(self, autorun(function persistPreferences() {
|
|
47
52
|
// observable.map doesn't JSON-serialize to an object on its own;
|
|
48
53
|
// fromEntries also reads every key so the autorun re-persists on
|
|
@@ -1670,7 +1670,6 @@ export declare function SessionTracksManagerSessionMixin(pluginManager: PluginMa
|
|
|
1670
1670
|
setPreferenceOverride(key: string, value: unknown): void;
|
|
1671
1671
|
clearPreferenceOverrides(): void;
|
|
1672
1672
|
clearPreferenceOverride(key: string): void;
|
|
1673
|
-
resetPreferenceChange(path: string[]): void;
|
|
1674
1673
|
setScrollZoom(flag: boolean): void;
|
|
1675
1674
|
setDisplayTypeDefault(displayType: string, slot: string, value: unknown): void;
|
|
1676
1675
|
setName(str: string): void;
|
|
@@ -1689,6 +1688,8 @@ export declare function SessionTracksManagerSessionMixin(pluginManager: PluginMa
|
|
|
1689
1688
|
pushSnackbarMessage(message: string, level?: import("@jbrowse/core/util").NotificationLevel, actions?: import("@jbrowse/core/util").SnackAction[]): void;
|
|
1690
1689
|
popSnackbarMessage(): import("@jbrowse/core/ui/SnackbarModel").SnackbarMessage | undefined;
|
|
1691
1690
|
removeSnackbarMessage(message: string): void;
|
|
1691
|
+
} & {
|
|
1692
|
+
afterAttach(): void;
|
|
1692
1693
|
} & {
|
|
1693
1694
|
getReferringMultiple(trackIds: string[]): Map<string, import("./ReferenceManagement.ts").ReferringNode[]>;
|
|
1694
1695
|
} & {
|
|
@@ -1700,6 +1701,8 @@ export declare function SessionTracksManagerSessionMixin(pluginManager: PluginMa
|
|
|
1700
1701
|
} & {
|
|
1701
1702
|
getTrackById(id: string): AnyConfigurationModel | undefined;
|
|
1702
1703
|
getTracksById(): Record<string, AnyConfigurationModel>;
|
|
1704
|
+
} & {
|
|
1705
|
+
afterAttach(): void;
|
|
1703
1706
|
} & {
|
|
1704
1707
|
addSessionTrackConf(trackConf: AnyConfiguration): any;
|
|
1705
1708
|
publishTrackConf(trackConf: AnyConfiguration): any;
|
|
@@ -4,6 +4,7 @@ import { expandLooseTrackConfig } from '@jbrowse/core/util/tracks';
|
|
|
4
4
|
import { applySnapshot, getSnapshot, isStateTreeNode, types, } from '@jbrowse/mobx-state-tree';
|
|
5
5
|
import { compareStructural } from 'mobx';
|
|
6
6
|
import { TracksManagerSessionMixin } from './Tracks.js';
|
|
7
|
+
import { assertNotReaddedDifferently } from './readdedTrackConf.js';
|
|
7
8
|
import { assertTrackConfOutlivesItsAssemblies } from './temporaryAssemblyTracks.js';
|
|
8
9
|
// jbrowse.tracks holds frozen plain objects (app-core, web/desktop) or MST
|
|
9
10
|
// config nodes (product-core, embedded react views); the delta math reads both
|
|
@@ -342,6 +343,13 @@ export function SessionTracksManagerSessionMixin(pluginManager) {
|
|
|
342
343
|
// and dropping its trackConfigDeltas override semantics.
|
|
343
344
|
const existing = self.getTrackById(trackId);
|
|
344
345
|
if (existing) {
|
|
346
|
+
if (self.sessionTracks.includes(existing)) {
|
|
347
|
+
assertNotReaddedDifferently(pluginManager, existing, {
|
|
348
|
+
...trackConf,
|
|
349
|
+
trackId,
|
|
350
|
+
type,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
345
353
|
return existing;
|
|
346
354
|
}
|
|
347
355
|
// sessionTracks is a typed MST array (unlike the frozen
|
package/esm/Session/TrackMenu.js
CHANGED
|
@@ -44,7 +44,7 @@ export function trackActionItems({ session, config, view, canEdit, isSessionOver
|
|
|
44
44
|
// visitor is served, one per click since the copy's stamped trackId defeats
|
|
45
45
|
// the dedupe, and a non-admin's lands as the dead `sessionTracks` entry
|
|
46
46
|
// ADR-084 removed the sweep for. Neither is worth offering, so the item says
|
|
47
|
-
// so instead of producing garbage; the
|
|
47
|
+
// so instead of producing garbage; the contract check on the adders
|
|
48
48
|
// is what found this.
|
|
49
49
|
//
|
|
50
50
|
// Greying a row tells nobody anything, and this reason is one nobody guesses:
|
|
@@ -86,7 +86,7 @@ export function trackActionItems({ session, config, view, canEdit, isSessionOver
|
|
|
86
86
|
onClick: () => {
|
|
87
87
|
const snap = makeCopy();
|
|
88
88
|
if (session.publishTrackConf(snap)) {
|
|
89
|
-
view.
|
|
89
|
+
void view.launchTrack(snap.trackId);
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
},
|
package/esm/Session/Tracks.d.ts
CHANGED
|
@@ -1638,7 +1638,6 @@ export declare function TracksManagerSessionMixin(pluginManager: PluginManager):
|
|
|
1638
1638
|
setPreferenceOverride(key: string, value: unknown): void;
|
|
1639
1639
|
clearPreferenceOverrides(): void;
|
|
1640
1640
|
clearPreferenceOverride(key: string): void;
|
|
1641
|
-
resetPreferenceChange(path: string[]): void;
|
|
1642
1641
|
setScrollZoom(flag: boolean): void;
|
|
1643
1642
|
setDisplayTypeDefault(displayType: string, slot: string, value: unknown): void;
|
|
1644
1643
|
setName(str: string): void;
|
|
@@ -1657,6 +1656,8 @@ export declare function TracksManagerSessionMixin(pluginManager: PluginManager):
|
|
|
1657
1656
|
pushSnackbarMessage(message: string, level?: import("@jbrowse/core/util").NotificationLevel, actions?: import("@jbrowse/core/util").SnackAction[]): void;
|
|
1658
1657
|
popSnackbarMessage(): import("@jbrowse/core/ui/SnackbarModel").SnackbarMessage | undefined;
|
|
1659
1658
|
removeSnackbarMessage(message: string): void;
|
|
1659
|
+
} & {
|
|
1660
|
+
afterAttach(): void;
|
|
1660
1661
|
} & {
|
|
1661
1662
|
getReferringMultiple(trackIds: string[]): Map<string, import("./ReferenceManagement.ts").ReferringNode[]>;
|
|
1662
1663
|
} & {
|
|
@@ -1689,6 +1690,8 @@ export declare function TracksManagerSessionMixin(pluginManager: PluginManager):
|
|
|
1689
1690
|
* @deprecated
|
|
1690
1691
|
*/
|
|
1691
1692
|
getTracksById(): Record<string, AnyConfigurationModel>;
|
|
1693
|
+
} & {
|
|
1694
|
+
afterAttach(): void;
|
|
1692
1695
|
} & {
|
|
1693
1696
|
/**
|
|
1694
1697
|
* #action
|
|
@@ -1734,10 +1737,6 @@ export declare function TracksManagerSessionMixin(pluginManager: PluginManager):
|
|
|
1734
1737
|
* session, which is the destination every such caller wanted: a track a
|
|
1735
1738
|
* plugin stands up for one user is not a catalog entry. Nothing in tree
|
|
1736
1739
|
* may call it — `no-restricted-syntax` says so.
|
|
1737
|
-
*
|
|
1738
|
-
* Spelled out rather than delegating through `this`, for the reason
|
|
1739
|
-
* `SessionTracks.addToSession` is a plain closure: an action whose return
|
|
1740
|
-
* type is inferred from a sibling's makes the pair circular.
|
|
1741
1740
|
*/
|
|
1742
1741
|
addTrackConf(trackConf: AnyConfiguration): any;
|
|
1743
1742
|
/**
|