@jbrowse/product-core 5.0.0-beta.2 → 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.
@@ -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
@@ -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
- * Here rather than filtered out of `getPreferenceChanges` by the dialog,
166
- * because the composite-key layout is this file's: a consumer that
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 runtime preference-override that currently differs from its
179
- * config/admin default, as `{ path, from, to }` rows — the exact set
180
- * `clearPreferenceOverrides` reverts. Backs the confirmation diff shown
181
- * before "Reset to defaults" (mirrors the per-track changes dialog). A
182
- * scalar pref (animationMode, scrollZoom) whose override equals the
183
- * default is omitted (reverting it is a no-op); each promoted
184
- * per-display-type default is always a difference from the un-promoted
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)
@@ -39,18 +39,6 @@ function parseDisplayTypeDefaultKey(key) {
39
39
  const [displayType, slot] = rest.split('\0');
40
40
  return displayType && slot ? { displayType, slot } : undefined;
41
41
  }
42
- // Head of the display path a promoted default takes in a `getPreferenceChanges`
43
- // row: `[DISPLAY_TYPE_DEFAULTS_PATH_HEAD, displayType, slot]`. Purely a
44
- // readable row label, deliberately distinct from the flat
45
- // `DISPLAY_TYPE_DEFAULT_PREFIX` storage key above.
46
- //
47
- // Module-private, like the storage key: both the producer
48
- // (`getPreferenceChanges`) and the consumer (`resetPreferenceChange`) are
49
- // methods on this model, so the two path shapes this file emits are the only
50
- // ones it has to undo. It used to be exported for the Preferences dialog to
51
- // match on and re-route back through `setDisplayTypeDefault`, which meant a
52
- // rename on one side alone would silently no-op that dialog's per-row reset.
53
- const DISPLAY_TYPE_DEFAULTS_PATH_HEAD = 'displayTypeDefaults';
54
42
  /**
55
43
  * #stateModel BaseSessionModel
56
44
  *
@@ -245,11 +233,8 @@ export function BaseSessionModel(_pluginManager) {
245
233
  * `{ displayType, slot, value }` — the inventory the Preferences dialog
246
234
  * lists and clears one at a time (`setDisplayTypeDefault(…, undefined)`).
247
235
  *
248
- * Here rather than filtered out of `getPreferenceChanges` by the dialog,
249
- * because the composite-key layout is this file's: a consumer that
250
- * recognized these rows by matching the path head is exactly the coupling
251
- * `DISPLAY_TYPE_DEFAULTS_PATH_HEAD` stopped being exported over, where a
252
- * 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.
253
238
  */
254
239
  getDisplayTypeDefaults() {
255
240
  const found = [];
@@ -263,31 +248,18 @@ export function BaseSessionModel(_pluginManager) {
263
248
  },
264
249
  /**
265
250
  * #method
266
- * every runtime preference-override that currently differs from its
267
- * config/admin default, as `{ path, from, to }` rows — the exact set
268
- * `clearPreferenceOverrides` reverts. Backs the confirmation diff shown
269
- * before "Reset to defaults" (mirrors the per-track changes dialog). A
270
- * scalar pref (animationMode, scrollZoom) whose override equals the
271
- * default is omitted (reverting it is a no-op); each promoted
272
- * per-display-type default is always a difference from the un-promoted
273
- * 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.
274
258
  */
275
259
  getPreferenceChanges() {
276
260
  const changes = [];
277
261
  for (const [key, value] of self.preferencesOverrides.entries()) {
278
- const dtd = parseDisplayTypeDefaultKey(key);
279
- if (dtd) {
280
- changes.push({
281
- path: [
282
- DISPLAY_TYPE_DEFAULTS_PATH_HEAD,
283
- dtd.displayType,
284
- dtd.slot,
285
- ],
286
- from: undefined,
287
- to: value,
288
- });
289
- }
290
- else {
262
+ if (!key.startsWith(DISPLAY_TYPE_DEFAULT_PREFIX)) {
291
263
  const dflt = this.getPreferenceDefault(key);
292
264
  if (value !== dflt) {
293
265
  changes.push({
@@ -418,27 +390,6 @@ export function BaseSessionModel(_pluginManager) {
418
390
  clearPreferenceOverride(key) {
419
391
  self.preferencesOverrides.delete(key);
420
392
  },
421
- /**
422
- * #action
423
- * revert one row emitted by `getPreferenceChanges`, addressed by its
424
- * display `path`. Backs the per-entry reset in the Preferences dialog's
425
- * "Reset to defaults" confirmation.
426
- *
427
- * Lives here rather than in that dialog because this model owns both path
428
- * shapes it has to undo: a promoted per-display-type default, whose row
429
- * path is a readable label over a flat composite storage key, and every
430
- * other override, whose path *is* its key. The dialog used to re-derive
431
- * the first case from an exported path-head constant.
432
- */
433
- resetPreferenceChange(path) {
434
- const [head, displayType, slot] = path;
435
- if (head === DISPLAY_TYPE_DEFAULTS_PATH_HEAD && displayType && slot) {
436
- this.setDisplayTypeDefault(displayType, slot, undefined);
437
- }
438
- else if (head) {
439
- this.clearPreferenceOverride(head);
440
- }
441
- },
442
393
  /**
443
394
  * #action
444
395
  * set the global scroll-to-zoom preference (see the `scrollZoom` getter)
@@ -1660,7 +1660,6 @@ export declare function MultipleViewsSessionMixin(pluginManager: PluginManager):
1660
1660
  setPreferenceOverride(key: string, value: unknown): void;
1661
1661
  clearPreferenceOverrides(): void;
1662
1662
  clearPreferenceOverride(key: string): void;
1663
- resetPreferenceChange(path: string[]): void;
1664
1663
  setScrollZoom(flag: boolean): void;
1665
1664
  setDisplayTypeDefault(displayType: string, slot: string, value: unknown): void;
1666
1665
  setName(str: string): void;
@@ -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
@@ -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;
@@ -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;
@@ -1702,6 +1701,8 @@ export declare function SessionTracksManagerSessionMixin(pluginManager: PluginMa
1702
1701
  } & {
1703
1702
  getTrackById(id: string): AnyConfigurationModel | undefined;
1704
1703
  getTracksById(): Record<string, AnyConfigurationModel>;
1704
+ } & {
1705
+ afterAttach(): void;
1705
1706
  } & {
1706
1707
  addSessionTrackConf(trackConf: AnyConfiguration): any;
1707
1708
  publishTrackConf(trackConf: AnyConfiguration): any;
@@ -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;
@@ -1691,6 +1690,8 @@ export declare function TracksManagerSessionMixin(pluginManager: PluginManager):
1691
1690
  * @deprecated
1692
1691
  */
1693
1692
  getTracksById(): Record<string, AnyConfigurationModel>;
1693
+ } & {
1694
+ afterAttach(): void;
1694
1695
  } & {
1695
1696
  /**
1696
1697
  * #action
@@ -1736,10 +1737,6 @@ export declare function TracksManagerSessionMixin(pluginManager: PluginManager):
1736
1737
  * session, which is the destination every such caller wanted: a track a
1737
1738
  * plugin stands up for one user is not a catalog entry. Nothing in tree
1738
1739
  * may call it — `no-restricted-syntax` says so.
1739
- *
1740
- * Spelled out rather than delegating through `this`, for the reason
1741
- * `SessionTracks.addToSession` is a plain closure: an action whose return
1742
- * type is inferred from a sibling's makes the pair circular.
1743
1740
  */
1744
1741
  addTrackConf(trackConf: AnyConfiguration): any;
1745
1742
  /**
@@ -1,6 +1,6 @@
1
1
  import { expandLooseTrackConfig } from '@jbrowse/core/util/tracks';
2
- import { types } from '@jbrowse/mobx-state-tree';
3
- import { computed } from 'mobx';
2
+ import { addDisposer, types } from '@jbrowse/mobx-state-tree';
3
+ import { autorun, computed } from 'mobx';
4
4
  import { BaseSessionModel, isBaseSession } from './BaseSession.js';
5
5
  import { isSessionWithConnections } from './Connections.js';
6
6
  import { ReferenceManagementSessionMixin } from './ReferenceManagement.js';
@@ -24,16 +24,11 @@ export function TracksManagerSessionMixin(pluginManager) {
24
24
  // One index trackId → config for all tracks, assembly sequences, and
25
25
  // connection tracks. Frozen jbrowse.tracks entries stay plain objects
26
26
  // here; hydration to MST nodes happens lazily in TrackConfigurationReference
27
- // on first access. Cached, so the N per-id lookups below share one rebuild
28
- // per change rather than each re-scanning.
29
- //
30
- // `keepAlive` is what makes that last sentence true, and it is load
31
- // bearing: an unobserved computed re-derives on EVERY read, and this one
32
- // walks every track, assembly and connection the session can resolve. A
33
- // reader inside a reaction or an MST action was already fine — MobX
34
- // caches within a batch, which is why the resolvers and `showTrackGeneric`
35
- // never showed it. Ranking search hits is the caller that is neither, and
36
- // it cost 33ms per search on a 2000-track config against 0.07ms held.
27
+ // on first access. Held hot by the autorun in afterAttach, so a reader
28
+ // outside any reaction (ranking search hits: 33ms per search unheld on a
29
+ // 2000-track config, 0.07ms held) gets the cache too. Not `keepAlive`: that
30
+ // subscription never ends, and it reaches jbrowse.tracks on the root, so
31
+ // it pinned every superseded session for the tab's life.
37
32
  const tracksByIdRecord = computed(() => {
38
33
  const temporaryAssemblies = 'temporaryAssemblies' in self
39
34
  ? self.temporaryAssemblies
@@ -59,7 +54,7 @@ export function TracksManagerSessionMixin(pluginManager) {
59
54
  e.config,
60
55
  ]),
61
56
  ]);
62
- }, { keepAlive: true });
57
+ }, { name: 'tracksByIdRecord' });
63
58
  // Per-id computed cache backing getTrackById. Resolving one track's config
64
59
  // subscribes only to that id's computed, so editing track A leaves track
65
60
  // B's observers untouched: an unedited id's entry keeps its object identity
@@ -102,24 +97,15 @@ export function TracksManagerSessionMixin(pluginManager) {
102
97
  };
103
98
  })
104
99
  .actions(self => ({
105
- /**
106
- * #action
107
- * Add a track config to *this session*.
108
- *
109
- * This mixin's session has no separate session-track store, so the
110
- * destination is the jbrowse config — which in the products that compose
111
- * it (desktop) is the single user's own file rather than something a
112
- * server hands other visitors, so the two scopes are the same place.
113
- * Defined here anyway so that every session has it: a feature standing a
114
- * track up on the user's behalf can then call one action everywhere
115
- * instead of asking which mixin it landed on.
116
- * `SessionTracksManagerSessionMixin` overrides it with the real
117
- * session-scoped store.
118
- */
119
- addSessionTrackConf(trackConf) {
100
+ afterAttach() {
101
+ addDisposer(self, autorun(() => {
102
+ self.getTracksById();
103
+ }));
104
+ },
105
+ }))
106
+ .actions(self => {
107
+ function addToSession(trackConf) {
120
108
  assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
121
- // the config's own adder appends without looking, so a re-add under a
122
- // known id used to leave two entries with the first one winning
123
109
  const expanded = expandLooseTrackConfig(trackConf, pluginManager);
124
110
  const { trackId, type } = expanded;
125
111
  const existing = self.getTrackById(trackId);
@@ -132,79 +118,93 @@ export function TracksManagerSessionMixin(pluginManager) {
132
118
  return existing;
133
119
  }
134
120
  return self.jbrowse.addTrackConf(trackConf);
135
- },
136
- /**
137
- * #action
138
- * Add a track config wherever *this user's* catalog edits belong — the
139
- * "Add track" workflows, where an admin adding a track means to add it
140
- * for the whole site. `SessionTracksManagerSessionMixin` overrides it to
141
- * send a non-admin's to the session instead; here there is only the one
142
- * destination.
143
- *
144
- * Anything that is not an Add-track workflow wants
145
- * `addSessionTrackConf`: a track a feature stands up on the user's behalf
146
- * — a search result, a computed consensus, a reconstruction's labels — is
147
- * not a catalog entry, and publishing one writes it into the config.json
148
- * every visitor is served, once per click.
149
- */
150
- publishTrackConf(trackConf) {
151
- assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
152
- return self.jbrowse.addTrackConf(trackConf);
153
- },
154
- /**
155
- * #action
156
- * Deprecated alias of `addSessionTrackConf`. Call that, or
157
- * `publishTrackConf`, which say which destination they mean.
158
- *
159
- * @deprecated
160
- *
161
- * Kept because a prebuilt plugin bundle reaches this by name at runtime
162
- * and cannot be recompiled — `jbrowse-plugin-protein3d` calls it, and a
163
- * member lookup that stops resolving throws nothing at all
164
- * (`pluginFacingSessionApi.test.ts` is the guard). It now means the
165
- * session, which is the destination every such caller wanted: a track a
166
- * plugin stands up for one user is not a catalog entry. Nothing in tree
167
- * may call it — `no-restricted-syntax` says so.
168
- *
169
- * Spelled out rather than delegating through `this`, for the reason
170
- * `SessionTracks.addToSession` is a plain closure: an action whose return
171
- * type is inferred from a sibling's makes the pair circular.
172
- */
173
- addTrackConf(trackConf) {
174
- assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
175
- return self.jbrowse.addTrackConf(trackConf);
176
- },
177
- /**
178
- * #action
179
- * Persist edited track config back to the in-memory jbrowse config. The
180
- * session-tracks mixin overrides this so a non-admin's edits become a
181
- * shareable session-track override instead.
182
- */
183
- updateTrackConfiguration(trackConf) {
184
- // an opened connection track lives in connectionTrackConfigs, not
185
- // jbrowse.tracks; persist its edit there (jbrowse.updateTrackConf would
186
- // no-op, since the track isn't in the config). Desktop uses this base
187
- // mixin, so without this branch a connection-track edit is lost on
188
- // reload.
189
- if (isSessionWithConnections(self) &&
190
- trackConf.trackId in self.connectionTrackConfigs) {
191
- self.updateConnectionTrackConfig(trackConf);
192
- }
193
- else {
194
- self.jbrowse.updateTrackConf(trackConf);
195
- }
196
- },
197
- /**
198
- * #action
199
- */
200
- deleteTrackConf(trackConf) {
201
- const { trackId } = trackConf;
202
- self.dereferenceTrack(trackId, self.getReferring(trackId));
203
- if (self.adminMode) {
204
- return self.jbrowse.deleteTrackConf(trackConf);
205
- }
206
- },
207
- }));
121
+ }
122
+ return {
123
+ /**
124
+ * #action
125
+ * Add a track config to *this session*.
126
+ *
127
+ * This mixin's session has no separate session-track store, so the
128
+ * destination is the jbrowse config — which in the products that compose
129
+ * it (desktop) is the single user's own file rather than something a
130
+ * server hands other visitors, so the two scopes are the same place.
131
+ * Defined here anyway so that every session has it: a feature standing a
132
+ * track up on the user's behalf can then call one action everywhere
133
+ * instead of asking which mixin it landed on.
134
+ * `SessionTracksManagerSessionMixin` overrides it with the real
135
+ * session-scoped store.
136
+ */
137
+ addSessionTrackConf(trackConf) {
138
+ return addToSession(trackConf);
139
+ },
140
+ /**
141
+ * #action
142
+ * Add a track config wherever *this user's* catalog edits belong — the
143
+ * "Add track" workflows, where an admin adding a track means to add it
144
+ * for the whole site. `SessionTracksManagerSessionMixin` overrides it to
145
+ * send a non-admin's to the session instead; here there is only the one
146
+ * destination.
147
+ *
148
+ * Anything that is not an Add-track workflow wants
149
+ * `addSessionTrackConf`: a track a feature stands up on the user's behalf
150
+ * — a search result, a computed consensus, a reconstruction's labels — is
151
+ * not a catalog entry, and publishing one writes it into the config.json
152
+ * every visitor is served, once per click.
153
+ */
154
+ publishTrackConf(trackConf) {
155
+ assertTrackConfOutlivesItsAssemblies(self, trackConf, 'jbrowse.tracks');
156
+ return self.jbrowse.addTrackConf(trackConf);
157
+ },
158
+ /**
159
+ * #action
160
+ * Deprecated alias of `addSessionTrackConf`. Call that, or
161
+ * `publishTrackConf`, which say which destination they mean.
162
+ *
163
+ * @deprecated
164
+ *
165
+ * Kept because a prebuilt plugin bundle reaches this by name at runtime
166
+ * and cannot be recompiled — `jbrowse-plugin-protein3d` calls it, and a
167
+ * member lookup that stops resolving throws nothing at all
168
+ * (`pluginFacingSessionApi.test.ts` is the guard). It now means the
169
+ * session, which is the destination every such caller wanted: a track a
170
+ * plugin stands up for one user is not a catalog entry. Nothing in tree
171
+ * may call it — `no-restricted-syntax` says so.
172
+ */
173
+ addTrackConf(trackConf) {
174
+ return addToSession(trackConf);
175
+ },
176
+ /**
177
+ * #action
178
+ * Persist edited track config back to the in-memory jbrowse config. The
179
+ * session-tracks mixin overrides this so a non-admin's edits become a
180
+ * shareable session-track override instead.
181
+ */
182
+ updateTrackConfiguration(trackConf) {
183
+ // an opened connection track lives in connectionTrackConfigs, not
184
+ // jbrowse.tracks; persist its edit there (jbrowse.updateTrackConf would
185
+ // no-op, since the track isn't in the config). Desktop uses this base
186
+ // mixin, so without this branch a connection-track edit is lost on
187
+ // reload.
188
+ if (isSessionWithConnections(self) &&
189
+ trackConf.trackId in self.connectionTrackConfigs) {
190
+ self.updateConnectionTrackConfig(trackConf);
191
+ }
192
+ else {
193
+ self.jbrowse.updateTrackConf(trackConf);
194
+ }
195
+ },
196
+ /**
197
+ * #action
198
+ */
199
+ deleteTrackConf(trackConf) {
200
+ const { trackId } = trackConf;
201
+ self.dereferenceTrack(trackId, self.getReferring(trackId));
202
+ if (self.adminMode) {
203
+ return self.jbrowse.deleteTrackConf(trackConf);
204
+ }
205
+ },
206
+ };
207
+ });
208
208
  }
209
209
  /** Type guard for SessionWithTracks */
210
210
  export function isSessionWithTracks(thing) {
package/esm/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type { AssertExtends, AssertNotAny } from './assertExtends.ts';
2
2
  export { asRoot, asSession } from './siblingCast.ts';
3
- export { scheduleDetachedDestroy } from './scheduleDetachedDestroy.ts';
3
+ export { scheduleDetachedDestroy } from '@jbrowse/core/util';
4
4
  export { BaseRootModelFactory, FormatAboutConfigSchemaFactory, FormatDetailsConfigSchemaFactory, HierarchicalConfigSchemaFactory, InternetAccountsRootModelMixin, PreferencesConfigSchemaFactory, createConfigModel, exportSessionMenuItem, importSessionMenuItem, isRootModel, newSessionMenuItem, openConnectionMenuItem, openTrackMenuItem, pluginStoreMenuItem, preferencesMenuItem, redoMenuItem, undoMenuItem, workspacesMenuItem, } from './RootModel/index.ts';
5
5
  export type { BaseRootModel, BaseRootModelType, RootConfigurationSnapshot, RootModelWithInternetAccounts, RootModelWithInternetAccountsType, } from './RootModel/index.ts';
6
6
  export { BaseSessionModel, ConnectionManagementSessionMixin, DrawerWidgetSessionMixin, MultipleViewsSessionMixin, PreferencesSessionMixin, ReferenceManagementSessionMixin, SessionTracksManagerSessionMixin, ThemeManagerSessionMixin, TrackMenuItemsSessionMixin, TrackMenuSessionMixin, TracksManagerSessionMixin, aboutTrackMenuItem, bakePromotedDefaultsIntoSnapshot, bakeSessionCascades, getShareableSessionSnapshot, copyTrackSnapshot, finalizeSession, isBaseSession, isSession, isSessionWithConnections, isSessionWithDrawerWidgets, isSessionWithMultipleViews, isSessionWithReferenceManagement, isSessionWithThemes, isSessionWithTracks, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './Session/index.ts';
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { asRoot, asSession } from './siblingCast.js';
2
- export { scheduleDetachedDestroy } from './scheduleDetachedDestroy.js';
2
+ export { scheduleDetachedDestroy } from '@jbrowse/core/util';
3
3
  export { BaseRootModelFactory, FormatAboutConfigSchemaFactory, FormatDetailsConfigSchemaFactory, HierarchicalConfigSchemaFactory, InternetAccountsRootModelMixin, PreferencesConfigSchemaFactory, createConfigModel, exportSessionMenuItem, importSessionMenuItem, isRootModel, newSessionMenuItem, openConnectionMenuItem, openTrackMenuItem, pluginStoreMenuItem, preferencesMenuItem, redoMenuItem, undoMenuItem, workspacesMenuItem, } from './RootModel/index.js';
4
4
  export { BaseSessionModel, ConnectionManagementSessionMixin, DrawerWidgetSessionMixin, MultipleViewsSessionMixin, PreferencesSessionMixin, ReferenceManagementSessionMixin, SessionTracksManagerSessionMixin, ThemeManagerSessionMixin, TrackMenuItemsSessionMixin, TrackMenuSessionMixin, TracksManagerSessionMixin, aboutTrackMenuItem, bakePromotedDefaultsIntoSnapshot, bakeSessionCascades, getShareableSessionSnapshot, copyTrackSnapshot, finalizeSession, isBaseSession, isSession, isSessionWithConnections, isSessionWithDrawerWidgets, isSessionWithMultipleViews, isSessionWithReferenceManagement, isSessionWithThemes, isSessionWithTracks, pluginExtraTrackItems, trackActionItems, trackActionMenuItems, trackListMenuItems, } from './Session/index.js';
5
5
  export { AboutDialog, Drawer, DrawerControls, DrawerHeader, DrawerWidget, DrawerWidgetSelector, ModalWidget, ModalWidgetAppBar, PreferencesDialog, WidgetBody, WidgetHeading, drawerGridTemplateColumns, } from './ui/index.js';
@@ -12,8 +12,11 @@ const HELD_NODES_KEY = 'heldForMissingPlugins';
12
12
  // widen the plugin ABI for nothing.
13
13
  function buildableElements(pluginManager, group) {
14
14
  return pluginManager.getElementTypesInGroup(group).filter(t => {
15
- const { stateModel } = t;
16
- return isType(stateModel) && isModelType(stateModel);
15
+ const element = t;
16
+ if (element.isStateModelLoaded === false) {
17
+ return false;
18
+ }
19
+ return isType(element.stateModel) && isModelType(element.stateModel);
17
20
  });
18
21
  }
19
22
  // optional/stripDefault/late/snapshotProcessor all report the type they wrap,
@@ -9,6 +9,7 @@ import { makeStyles } from '@jbrowse/core/util/tss-react';
9
9
  import { isStateTreeNode } from '@jbrowse/mobx-state-tree';
10
10
  import { observer } from 'mobx-react';
11
11
  import AssemblyInfoPanel from './AssemblyInfoPanel.js';
12
+ import DescriptionPanel from './DescriptionPanel.js';
12
13
  import FileInfoPanel from './FileInfoPanel.js';
13
14
  import HeaderButtons from './HeaderButtons.js';
14
15
  import RefNameInfoDialog from './RefNameInfoDialog.js';
@@ -49,7 +50,7 @@ const AboutDialogContents = observer(function AboutDialogContents({ config, sess
49
50
  session,
50
51
  pluginManager,
51
52
  });
52
- return (_jsxs("div", { className: classes.content, children: [_jsxs(BaseCard, { title: "Configuration", children: [_jsx(HeaderButtons, { conf: conf, hideUris: hideUris, fromDisplayTypeDefaults: fromDisplayTypeDefaults, setShowRefNames: setShowRefNames }), _jsx(Attributes, { attributes: shown, omit: [...hideFields, 'metadata'], hideUris: hideUris })] }), shown.metadata ? (_jsx(BaseCard, { title: "Metadata", children: _jsx(Attributes, { attributes: shown.metadata, hideUris: hideUris }) })) : null, _jsx(AssemblyInfoPanel, { config: config, session: session, hideUris: hideUris }), _jsx(PluggableComponents, { pluginManager: pluginManager,
53
+ return (_jsxs("div", { className: classes.content, children: [_jsxs(BaseCard, { title: "Configuration", children: [_jsx(HeaderButtons, { conf: conf, hideUris: hideUris, fromDisplayTypeDefaults: fromDisplayTypeDefaults, setShowRefNames: setShowRefNames }), _jsx(Attributes, { attributes: shown, omit: [...hideFields, 'metadata'], hideUris: hideUris })] }), shown.metadata ? (_jsx(BaseCard, { title: "Metadata", children: _jsx(Attributes, { attributes: shown.metadata, hideUris: hideUris }) })) : null, _jsx(DescriptionPanel, { config: config, session: session }), _jsx(AssemblyInfoPanel, { config: config, session: session, hideUris: hideUris }), _jsx(PluggableComponents, { pluginManager: pluginManager,
53
54
  /** #extensionPoint Core-extraAboutPanel | sync | Add extra panels to a track's About dialog */
54
55
  name: "Core-extraAboutPanel", props: { session, config } }), hideUris ? null : _jsx(FileInfoPanel, { config: config, session: session }), showRefNames ? (_jsx(RefNameInfoDialog, { session: session, config: config, onClose: () => {
55
56
  setShowRefNames(false);
@@ -0,0 +1,3 @@
1
+ import type { AboutPanelProps } from './util.ts';
2
+ declare const DescriptionPanel: ({ config, }: AboutPanelProps) => import("react").JSX.Element | null;
3
+ export default DescriptionPanel;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import BaseCard from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail/BaseCard';
3
+ import { ErrorBanner, LoadingEllipses, SanitizedHTML } from '@jbrowse/core/ui';
4
+ import { openLocation } from '@jbrowse/core/util/io';
5
+ import { makeStyles } from '@jbrowse/core/util/tss-react';
6
+ import { useFetch } from '@jbrowse/core/util/useFetch';
7
+ import { Link, Typography } from '@mui/material';
8
+ import { observer } from 'mobx-react';
9
+ import { getDescriptionHtmlUrl, rebaseDescriptionHtml, } from './descriptionHtml.js';
10
+ const useStyles = makeStyles()(theme => ({
11
+ provenance: {
12
+ marginBottom: theme.spacing(1),
13
+ },
14
+ }));
15
+ const DescriptionCard = observer(function DescriptionCard({ url, }) {
16
+ const { classes } = useStyles();
17
+ const { data: html, error, isLoading, } = useFetch(['trackDescriptionHtml', url], async (_name, url) => rebaseDescriptionHtml(await openLocation({ uri: url, locationType: 'UriLocation' }).readFile('utf8'), url));
18
+ return (_jsxs(BaseCard, { title: "Description", children: [_jsxs(Typography, { variant: "caption", color: "textSecondary", component: "div", className: classes.provenance, children: ["From the track hub, written for the UCSC Genome Browser \u2014 some display options it describes differ in JBrowse.", ' ', _jsx(Link, { href: url, target: "_blank", rel: "noopener noreferrer", children: "View original" })] }), error ? (_jsx(ErrorBanner, { error: error })) : isLoading || html === undefined ? (_jsx(LoadingEllipses, { message: "Loading description" })) : (_jsx(SanitizedHTML, { html: html }))] }));
19
+ });
20
+ const DescriptionPanel = observer(function DescriptionPanel({ config, }) {
21
+ const url = getDescriptionHtmlUrl(config);
22
+ return url ? _jsx(DescriptionCard, { url: url }) : null;
23
+ });
24
+ export default DescriptionPanel;
@@ -1,33 +1,20 @@
1
+ import type { DisplayDefaultsSession } from './displayDefaultChanges.ts';
1
2
  import type PluginManager from '@jbrowse/core/PluginManager';
2
- export interface DisplayDefaultsSession {
3
- getDisplayTypeDefaults: () => {
4
- displayType: string;
5
- slot: string;
6
- value: unknown;
7
- }[];
8
- setDisplayTypeDefault: (displayType: string, slot: string, value: unknown) => void;
9
- }
3
+ export type { DisplayDefaultsSession } from './displayDefaultChanges.ts';
10
4
  /**
11
5
  * The inventory of session-wide display-type defaults: every value the user has
12
6
  * pinned from a track menu, what it overrides, and a button to clear one.
13
7
  *
14
8
  * The pin is easy to set and, until this, hard to find again. A pinned default
15
9
  * shows on the track-selector badge of any **open** track it moves — but one
16
- * affecting nothing currently open, one promoted to a value equal to the slot's
17
- * base, and one that no longer resolves at all (a stale value the cascade
18
- * refuses, or a display type whose plugin is gone) appear nowhere. The only
19
- * place that listed them was the "Reset preferences to defaults" confirmation,
20
- * which is a destructive dialog to have to open in order to read something.
10
+ * affecting nothing currently open, and one that no longer resolves at all (a
11
+ * stale value the cascade refuses, or a display type whose plugin is gone)
12
+ * appear nowhere. The only place that listed them was the "Reset preferences to
13
+ * defaults" confirmation, which is a destructive dialog to have to open in
14
+ * order to read something.
21
15
  *
22
16
  * The empty state is deliberate rather than rendering nothing: it is the one
23
17
  * place the app says the capability exists to someone who has never used it.
24
- *
25
- * **The slot keeps its schema name**, while the display type gets its authored
26
- * one. A slot has no menu label to borrow — which row a setting is offered on is
27
- * a menu fact and declaring the slot promotable is a schema fact, and nothing
28
- * joins them (that split is why `promotableSlotsWithoutPin` has to walk a built
29
- * menu at all). A raw slot name is also what the config editor labels its fields
30
- * with, so the two surfaces agree.
31
18
  */
32
19
  declare const DisplayDefaultsSection: ({ session, pluginManager, }: {
33
20
  session: DisplayDefaultsSession;