@jbrowse/core 5.0.0-beta.10 → 5.0.0-beta.13

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.
Files changed (145) hide show
  1. package/README.md +210 -80
  2. package/esm/BaseFeatureWidget/BaseFeatureDetail/ArrayValue.js +0 -2
  3. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.d.ts +3 -0
  4. package/esm/BaseFeatureWidget/BaseFeatureDetail/Attributes.js +14 -1
  5. package/esm/BaseFeatureWidget/BaseFeatureDetail/BasicValue.js +22 -6
  6. package/esm/BaseFeatureWidget/BaseFeatureDetail/jexlFilterActions.js +3 -4
  7. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.d.ts +2 -0
  8. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeatureDetails.js +5 -5
  9. package/esm/BaseFeatureWidget/SequenceFeatureDetails/SequenceFeaturePanel.js +1 -1
  10. package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.d.ts +3 -1
  11. package/esm/BaseFeatureWidget/SequenceFeatureDetails/dialogs/FeatureSequenceDialog.js +2 -2
  12. package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.d.ts +2 -1
  13. package/esm/BaseFeatureWidget/SequenceFeatureDetails/featureTypeUtil.js +7 -14
  14. package/esm/BaseFeatureWidget/SequenceFeatureDetails/seqtypes/CDNASequence.js +3 -3
  15. package/esm/BaseFeatureWidget/SequenceFeatureDetails/useSequenceData.js +8 -5
  16. package/esm/BaseFeatureWidget/stateModelFactory.d.ts +7 -0
  17. package/esm/BaseFeatureWidget/stateModelFactory.js +11 -0
  18. package/esm/BaseFeatureWidget/types.d.ts +2 -0
  19. package/esm/CorePlugin.js +32 -1
  20. package/esm/PluginManager.d.ts +6 -3
  21. package/esm/PluginManager.js +6 -3
  22. package/esm/ReExports/coreModules.generated.js +291 -283
  23. package/esm/ReExports/coreWorkerModules.generated.js +288 -281
  24. package/esm/ReExports/list.js +10 -2
  25. package/esm/TextSearch/TextSearchManager.d.ts +3 -3
  26. package/esm/TextSearch/TextSearchManager.js +6 -13
  27. package/esm/configuration/configurationSchema.d.ts +33 -95
  28. package/esm/configuration/configurationSchema.js +50 -218
  29. package/esm/configuration/configurationSlot.d.ts +18 -64
  30. package/esm/configuration/configurationSlot.js +31 -97
  31. package/esm/configuration/formatDetailsConfigSchema.js +2 -0
  32. package/esm/configuration/getConf.d.ts +7 -36
  33. package/esm/configuration/getConf.js +7 -37
  34. package/esm/configuration/index.d.ts +5 -2
  35. package/esm/configuration/index.js +3 -1
  36. package/esm/configuration/partitionAdvanced.d.ts +12 -0
  37. package/esm/configuration/partitionAdvanced.js +42 -0
  38. package/esm/configuration/plot.d.ts +67 -0
  39. package/esm/configuration/plot.js +175 -0
  40. package/esm/configuration/readConfObject.d.ts +15 -17
  41. package/esm/configuration/readConfObject.js +19 -50
  42. package/esm/configuration/schemaTypes.js +5 -1
  43. package/esm/configuration/snapshotPreprocess.js +1 -3
  44. package/esm/configuration/types.d.ts +60 -159
  45. package/esm/pluggableElementTypes/DisplayType.d.ts +14 -0
  46. package/esm/pluggableElementTypes/DisplayType.js +13 -0
  47. package/esm/pluggableElementTypes/RpcMethodType.d.ts +7 -0
  48. package/esm/pluggableElementTypes/RpcMethodType.js +9 -0
  49. package/esm/pluggableElementTypes/WidgetType.d.ts +6 -0
  50. package/esm/pluggableElementTypes/WidgetType.js +6 -0
  51. package/esm/pluggableElementTypes/addMenuItems.d.ts +2 -1
  52. package/esm/pluggableElementTypes/addMenuItems.js +2 -1
  53. package/esm/pluggableElementTypes/index.js +1 -8
  54. package/esm/pluggableElementTypes/models/BaseDisplayModel.d.ts +58 -1
  55. package/esm/pluggableElementTypes/models/BaseDisplayModel.js +84 -2
  56. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +2 -4
  57. package/esm/pluggableElementTypes/models/BaseTrackModel.js +2 -4
  58. package/esm/pluggableElementTypes/models/BaseViewModel.d.ts +2 -2
  59. package/esm/pluggableElementTypes/models/BaseViewModel.js +1 -1
  60. package/esm/pluggableElementTypes/models/baseTrackConfig.js +3 -0
  61. package/esm/rpc/RpcRegistry.d.ts +2 -1
  62. package/esm/rpc/byteBudget.d.ts +7 -0
  63. package/esm/rpc/byteBudget.js +10 -0
  64. package/esm/rpc/coreRpcMethods.d.ts +1 -1
  65. package/esm/rpc/coreRpcMethods.js +1 -1
  66. package/esm/rpc/explainTransferError.js +3 -3
  67. package/esm/rpc/methods/CoreLoadTrackCode.d.ts +17 -0
  68. package/esm/rpc/methods/CoreLoadTrackCode.js +24 -0
  69. package/esm/ui/AppLogo.js +43 -3
  70. package/esm/ui/JexlFilterDialog.d.ts +1 -1
  71. package/esm/ui/JexlFilterDialog.js +2 -3
  72. package/esm/ui/buildExtraTrackMenuItems.d.ts +2 -2
  73. package/esm/ui/colors.d.ts +41 -30
  74. package/esm/ui/colors.js +81 -44
  75. package/esm/ui/multiTrackMenuItems.d.ts +2 -2
  76. package/esm/util/TimeTraveller.d.ts +3 -0
  77. package/esm/util/TimeTraveller.js +34 -2
  78. package/esm/util/alignedBytes.d.ts +19 -0
  79. package/esm/util/alignedBytes.js +37 -0
  80. package/esm/util/analytics.d.ts +5 -6
  81. package/esm/util/analytics.js +5 -3
  82. package/esm/util/baseDisplayConfig.d.ts +8 -0
  83. package/esm/util/baseDisplayConfig.js +27 -0
  84. package/esm/util/bezierConnector.js +5 -1
  85. package/esm/util/cachedSetup.d.ts +16 -6
  86. package/esm/util/cachedSetup.js +108 -38
  87. package/esm/util/categoricalField.d.ts +23 -3
  88. package/esm/util/categoricalField.js +36 -3
  89. package/esm/util/cellMatches.d.ts +23 -0
  90. package/esm/util/cellMatches.js +264 -0
  91. package/esm/util/cellsStep.d.ts +27 -0
  92. package/esm/util/cellsStep.js +439 -0
  93. package/esm/util/color-bits/parse.js +22 -11
  94. package/esm/util/colorRamp.js +6 -0
  95. package/esm/util/colorScale.d.ts +9 -2
  96. package/esm/util/colorScale.js +33 -2
  97. package/esm/util/colorSchemes.d.ts +4 -2
  98. package/esm/util/colorSchemes.js +4 -1
  99. package/esm/util/copyToClipboard.js +8 -1
  100. package/esm/util/createAbortRotation.js +3 -3
  101. package/esm/util/featureTransforms.d.ts +7 -6
  102. package/esm/util/featureTransforms.js +131 -479
  103. package/esm/util/fetchPhases.d.ts +1 -1
  104. package/esm/util/formatBytes.js +1 -1
  105. package/esm/util/formatGuessers.d.ts +25 -14
  106. package/esm/util/formatGuessers.js +42 -27
  107. package/esm/util/formatRelativeTime.js +1 -1
  108. package/esm/util/getLocationUri.d.ts +2 -1
  109. package/esm/util/getLocationUri.js +8 -2
  110. package/esm/util/groupKeys.d.ts +4 -5
  111. package/esm/util/groupKeys.js +7 -6
  112. package/esm/util/intervals.js +9 -4
  113. package/esm/util/io/index.js +3 -1
  114. package/esm/util/isoformRank.d.ts +39 -0
  115. package/esm/util/isoformRank.js +118 -0
  116. package/esm/util/jexlFilters.d.ts +31 -49
  117. package/esm/util/jexlFilters.js +29 -39
  118. package/esm/util/locString.js +5 -2
  119. package/esm/util/markEncoding.js +48 -8
  120. package/esm/util/markEncodingTypes.d.ts +11 -2
  121. package/esm/util/quantileExtent.d.ts +22 -0
  122. package/esm/util/quantileExtent.js +24 -1
  123. package/esm/util/springAnimate.js +1 -1
  124. package/esm/util/stepTables.d.ts +14 -0
  125. package/esm/util/stepTables.js +38 -4
  126. package/esm/util/systemColorScheme.d.ts +4 -0
  127. package/esm/util/systemColorScheme.js +21 -0
  128. package/esm/util/tabix.d.ts +7 -3
  129. package/esm/util/tabix.js +3 -3
  130. package/esm/util/temporaryAssembly.d.ts +2 -2
  131. package/esm/util/temporaryAssembly.js +2 -3
  132. package/esm/util/trackConfigDelta.d.ts +1 -1
  133. package/esm/util/tracks.d.ts +28 -45
  134. package/esm/util/tracks.js +71 -82
  135. package/esm/util/types/index.d.ts +15 -7
  136. package/esm/util/types/index.js +21 -2
  137. package/esm/util/unknownSnapshotKeys.d.ts +3 -1
  138. package/esm/util/unknownSnapshotKeys.js +5 -11
  139. package/esm/util/usePanZoom.js +10 -3
  140. package/esm/util/useRafCommit.js +1 -0
  141. package/esm/util/useRowVirtualScroll.d.ts +1 -0
  142. package/esm/util/withFeatureDetails.js +4 -0
  143. package/package.json +13 -9
  144. package/esm/rpc/methods/CoreLoadAdapterCode.d.ts +0 -13
  145. package/esm/rpc/methods/CoreLoadAdapterCode.js +0 -16
@@ -1,5 +1,6 @@
1
1
  import { hasParent, isAlive, types } from '@jbrowse/mobx-state-tree';
2
- import { applyConfSettings, getConf } from '../../configuration/index.js';
2
+ import { applyConfSettings, getConfigurationSchemaMetadata, getConf, setConf, } from '../../configuration/index.js';
3
+ import { liftPlot as liftPlotDraft, plotKeysOf, plotOf, plotWrites as plotWritesOf, schemaPlotProblems, } from '../../configuration/plot.js';
3
4
  import { getContainingTrack, getEnv, statusFraction, statusMessageText, } from '../../util/index.js';
4
5
  import { ElementId } from '../../util/types/mst.js';
5
6
  import { displaySetterName } from '../../util/unknownSnapshotKeys.js';
@@ -198,7 +199,7 @@ function stateModelFactory() {
198
199
  * `installClearHoverOnViewportChange` calls.
199
200
  *
200
201
  * A display that STORES its hover owes an override; one that derives it
201
- * from the live pointer (MAF, Hi-C, LD) owes nothing, and the default
202
+ * from the live pointer (Hi-C, LD) owes nothing, and the default
202
203
  * costs it nothing. Declared here so the clear can be installed for every
203
204
  * display rather than remembered per display — forgetting it is the
204
205
  * failure ARCHITECTURE.md's stored-hover section is about, and it used to
@@ -272,6 +273,87 @@ function stateModelFactory() {
272
273
  }
273
274
  return { applied, unapplied, failed };
274
275
  },
276
+ }))
277
+ .views(self => ({
278
+ /**
279
+ * #getter
280
+ * The grammar settings this display's config declares, by slot name
281
+ * (`PLOT_VOCABULARY`): what "Edit plot..." edits. Empty on a display
282
+ * with none, which then offers no editor.
283
+ */
284
+ get plotKeys() {
285
+ return plotKeysOf(self.configuration);
286
+ },
287
+ /**
288
+ * #getter
289
+ * Those settings as declared, defaults left off and a shorthand written
290
+ * as a config file writes it. An agent edits a `structuredClone` of it,
291
+ * checks it with `plotProblems` and hands it to `applyPlot`.
292
+ */
293
+ get plot() {
294
+ return plotOf(self.configuration);
295
+ },
296
+ /**
297
+ * #getter
298
+ * The worked examples this display type declares, which "Edit plot..."
299
+ * offers as buttons.
300
+ */
301
+ get plotExamples() {
302
+ const { pluginManager } = getEnv(self);
303
+ return pluginManager.getDisplayType(self.type).plotExamples;
304
+ },
305
+ /**
306
+ * #getter
307
+ * This display type's page in the config reference, which lists every
308
+ * setting it takes.
309
+ */
310
+ get configDocsUrl() {
311
+ const name = getConfigurationSchemaMetadata(self.configuration)?.name;
312
+ return name
313
+ ? `https://jbrowse.org/jb2/docs/config/${name.toLowerCase()}/`
314
+ : 'https://jbrowse.org/jb2/docs/config_guide/';
315
+ },
316
+ /**
317
+ * #method
318
+ * A draft as the config would hold it, merged over `plot`, throwing what
319
+ * a config file would be refused for. Nothing on the display changes.
320
+ */
321
+ liftPlot(draft) {
322
+ return liftPlotDraft(self.configuration, draft);
323
+ },
324
+ /**
325
+ * #method
326
+ * What a draft would report once applied, throwing a refusal as
327
+ * `liftPlot` does: what its colour objects and `scales.y` say together
328
+ * that cannot be drawn as written. A display with a rule list over its
329
+ * settings overrides it; the draft still applies, and draws what it can.
330
+ */
331
+ plotProblems(draft) {
332
+ return schemaPlotProblems(liftPlotDraft(self.configuration, draft));
333
+ },
334
+ /**
335
+ * #method
336
+ * What applying a draft would write, by setting: the lifted value where
337
+ * it differs from `plot`, `null` where it resets one. Throws a refusal.
338
+ */
339
+ plotWrites(draft) {
340
+ return plotWritesOf(self.configuration, draft);
341
+ },
342
+ }))
343
+ .actions(self => ({
344
+ /**
345
+ * #action
346
+ * Write a draft over `plot`: each setting it changes is replaced whole,
347
+ * so a member left out of an object goes back to its default, and `null`
348
+ * resets the setting. A draft the schema refuses, or naming a setting
349
+ * outside the plot, throws before anything is written.
350
+ */
351
+ applyPlot(draft) {
352
+ const { configuration } = self;
353
+ for (const [key, value] of Object.entries(self.plotWrites(draft))) {
354
+ setConf(configuration, key, value);
355
+ }
356
+ },
275
357
  }));
276
358
  }
277
359
  export const BaseDisplay = stateModelFactory();
@@ -200,10 +200,8 @@ export declare function createBaseTrackModel(pm: PluginManager, trackType: strin
200
200
  * completely untouched ones — `reaction` only fires on an actual change.
201
201
  *
202
202
  * `equals: compareStructural` is load-bearing, not an optimization:
203
- * `self.configuration` is a re-resolving reference, and persisting a save
204
- * can swap the resolved node identity (the delta path reconciles in place
205
- * but still churns once, and a session without deltas replaces the frozen
206
- * `jbrowse.tracks` entry, rehydrating a brand-new MST node every write).
203
+ * `self.configuration` is a re-resolving reference, and an undo or a
204
+ * session restore swaps the resolved node for a new working copy.
207
205
  * Referential comparison would treat every such swap as a fresh change
208
206
  * and re-fire the save, an unbounded debounced loop. Structural
209
207
  * comparison settles once the content stops changing.
@@ -324,10 +324,8 @@ export function createBaseTrackModel(pm, trackType, baseTrackConfig) {
324
324
  * completely untouched ones — `reaction` only fires on an actual change.
325
325
  *
326
326
  * `equals: compareStructural` is load-bearing, not an optimization:
327
- * `self.configuration` is a re-resolving reference, and persisting a save
328
- * can swap the resolved node identity (the delta path reconciles in place
329
- * but still churns once, and a session without deltas replaces the frozen
330
- * `jbrowse.tracks` entry, rehydrating a brand-new MST node every write).
327
+ * `self.configuration` is a re-resolving reference, and an undo or a
328
+ * session restore swaps the resolved node for a new working copy.
331
329
  * Referential comparison would treat every such swap as a fresh change
332
330
  * and re-fire the save, an unbounded debounced loop. Structural
333
331
  * comparison settles once the content stops changing.
@@ -29,7 +29,7 @@ declare const BaseViewModel: import("@jbrowse/mobx-state-tree").IModelType<{
29
29
  *
30
30
  * `ViewContainer` mounts a view's body only while an IntersectionObserver
31
31
  * says it is on screen, to hold the app under the WebGL2 context ceiling
32
- * (`reference/GPU_CONTEXT_BUDGET.md`). A view below the fold therefore has
32
+ * (`reference/GPU_PORTABILITY.md`). A view below the fold therefore has
33
33
  * no canvas, so nothing ever calls `markCanvasDrawn` and the pre-first-paint
34
34
  * term of `displayPhase` pins every display in it at `loading` with nothing
35
35
  * left to resolve it — which parks `[data-app-phase="ready"]` for the whole
@@ -169,7 +169,7 @@ export declare const BaseViewModelWithDisplayedRegions: import("@jbrowse/mobx-st
169
169
  *
170
170
  * `ViewContainer` mounts a view's body only while an IntersectionObserver
171
171
  * says it is on screen, to hold the app under the WebGL2 context ceiling
172
- * (`reference/GPU_CONTEXT_BUDGET.md`). A view below the fold therefore has
172
+ * (`reference/GPU_PORTABILITY.md`). A view below the fold therefore has
173
173
  * no canvas, so nothing ever calls `markCanvasDrawn` and the pre-first-paint
174
174
  * term of `displayPhase` pins every display in it at `loading` with nothing
175
175
  * left to resolve it — which parks `[data-app-phase="ready"]` for the whole
@@ -54,7 +54,7 @@ const BaseViewModel = types
54
54
  *
55
55
  * `ViewContainer` mounts a view's body only while an IntersectionObserver
56
56
  * says it is on screen, to hold the app under the WebGL2 context ceiling
57
- * (`reference/GPU_CONTEXT_BUDGET.md`). A view below the fold therefore has
57
+ * (`reference/GPU_PORTABILITY.md`). A view below the fold therefore has
58
58
  * no canvas, so nothing ever calls `markCanvasDrawn` and the pre-first-paint
59
59
  * term of `displayPhase` pins every display in it at `loading` with nothing
60
60
  * left to resolve it — which parks `[data-app-phase="ready"]` for the whole
@@ -177,6 +177,7 @@ export function createBaseTrackConfig(pluginManager) {
177
177
  type: 'stringArray',
178
178
  description: 'list of which feature attributes to index for text searching',
179
179
  defaultValue: ['Name', 'ID', 'symbol'],
180
+ advanced: true,
180
181
  },
181
182
  /**
182
183
  * #slot textSearching.indexingFeatureTypesToExclude
@@ -185,6 +186,7 @@ export function createBaseTrackConfig(pluginManager) {
185
186
  type: 'stringArray',
186
187
  description: 'list of feature types to exclude in text search index',
187
188
  defaultValue: ['CDS', 'exon'],
189
+ advanced: true,
188
190
  },
189
191
  /**
190
192
  * #slot textSearching.indexingFeatureTypesToInclude
@@ -207,6 +209,7 @@ export function createBaseTrackConfig(pluginManager) {
207
209
  type: 'stringArray',
208
210
  description: 'the only feature types to index; empty means index every type not excluded',
209
211
  defaultValue: [],
212
+ advanced: true,
210
213
  },
211
214
  /**
212
215
  * #slot textSearching.textSearchAdapter
@@ -100,9 +100,10 @@ export interface RpcRegistry {
100
100
  args: Record<string, unknown>;
101
101
  return: void;
102
102
  };
103
- CoreLoadAdapterCode: {
103
+ CoreLoadTrackCode: {
104
104
  args: {
105
105
  adapterTypes: string[];
106
+ rpcMethods: string[];
106
107
  };
107
108
  return: void;
108
109
  };
@@ -33,6 +33,13 @@ export interface RegionTooLargeResult {
33
33
  partial?: boolean;
34
34
  }
35
35
  export declare function isRegionRefused(result: unknown): result is RegionTooLargeResult;
36
+ /**
37
+ * The thrown form of the same refusal, for an adapter whose limit only a fetch
38
+ * can find, such as a graph's node count. Marked by an own `regionTooLarge`
39
+ * property, which crosses the worker boundary, so an adapter's own error class
40
+ * opts in without importing anything.
41
+ */
42
+ export declare function isRegionTooLargeError(error: unknown): error is Error;
36
43
  /** Undefined, never 0, when no region could be measured. */
37
44
  export declare function largestRegionBytes(perRegion: (number | undefined)[]): number | undefined;
38
45
  /** A non-positive declared limit means "no opinion" (htsget reports 0). */
@@ -3,6 +3,16 @@ import { updateStatus } from '../util/progress.js';
3
3
  export function isRegionRefused(result) {
4
4
  return (typeof result === 'object' && result !== null && 'regionTooLarge' in result);
5
5
  }
6
+ /**
7
+ * The thrown form of the same refusal, for an adapter whose limit only a fetch
8
+ * can find, such as a graph's node count. Marked by an own `regionTooLarge`
9
+ * property, which crosses the worker boundary, so an adapter's own error class
10
+ * opts in without importing anything.
11
+ */
12
+ export function isRegionTooLargeError(error) {
13
+ return (error instanceof Error &&
14
+ error.regionTooLarge === true);
15
+ }
6
16
  /** Undefined, never 0, when no region could be measured. */
7
17
  export function largestRegionBytes(perRegion) {
8
18
  let largest;
@@ -6,7 +6,7 @@ export { default as CoreGetEncodedLayers } from './methods/CoreGetEncodedLayers.
6
6
  export { default as CoreGetEncodedFeature } from './methods/CoreGetEncodedFeature.ts';
7
7
  export { default as CoreGetSequence } from './methods/CoreGetSequence.ts';
8
8
  export { default as CoreFreeResources } from './methods/CoreFreeResources.ts';
9
- export { default as CoreLoadAdapterCode } from './methods/CoreLoadAdapterCode.ts';
9
+ export { default as CoreLoadTrackCode } from './methods/CoreLoadTrackCode.ts';
10
10
  export { default as CoreGetRegionByteEstimate } from './methods/CoreGetRegionByteEstimate.ts';
11
11
  export { default as CoreGetFeatureDensity } from './methods/CoreGetFeatureDensity.ts';
12
12
  export { default as CoreGetRegions } from './methods/CoreGetRegions.ts';
@@ -6,7 +6,7 @@ export { default as CoreGetEncodedLayers } from './methods/CoreGetEncodedLayers.
6
6
  export { default as CoreGetEncodedFeature } from './methods/CoreGetEncodedFeature.js';
7
7
  export { default as CoreGetSequence } from './methods/CoreGetSequence.js';
8
8
  export { default as CoreFreeResources } from './methods/CoreFreeResources.js';
9
- export { default as CoreLoadAdapterCode } from './methods/CoreLoadAdapterCode.js';
9
+ export { default as CoreLoadTrackCode } from './methods/CoreLoadTrackCode.js';
10
10
  export { default as CoreGetRegionByteEstimate } from './methods/CoreGetRegionByteEstimate.js';
11
11
  export { default as CoreGetFeatureDensity } from './methods/CoreGetFeatureDensity.js';
12
12
  export { default as CoreGetRegions } from './methods/CoreGetRegions.js';
@@ -14,9 +14,9 @@
14
14
  // sentence this appends to.
15
15
  //
16
16
  // Only runs on the error path.
17
- // The deepest RPC result in the tree is the regular multi-sample variant one:
18
- // `perRegionCellData.0.featureGenotypeMap.<featureId>.genotypeCodes` — four
19
- // containers down. The alignments result is three (`groups.3.data.mismatchStarts`),
17
+ // The deepest RPC result in the tree is the multi-sample variant one:
18
+ // `perRegionCellData.0.featureInfo.<index>.genotypeCodes` — four containers
19
+ // down. The alignments result is three (`groups.3.data.mismatchStarts`),
20
20
  // synteny two (`instanceData.bp1`, `attributes.identity`) and most are one. The
21
21
  // walk is capped rather than unbounded because a payload also carries plain data
22
22
  // — a featureIds array of half a million strings — and there is no reason to
@@ -0,0 +1,17 @@
1
+ import RpcMethodType from '../../pluggableElementTypes/RpcMethodType.ts';
2
+ import type { RpcExecuteArgs } from '../RpcRegistry.ts';
3
+ /**
4
+ * Load the code a track's first request runs in the worker it is about to use:
5
+ * its adapter classes and the RPC methods its display names. Each arrives by
6
+ * dynamic import on that request otherwise, after the view lays out, the
7
+ * assembly loads and the request's refNames resolve.
8
+ *
9
+ * At launch rather than at worker boot: until `parallelizeChunkLoading` is
10
+ * installed a classic worker loads chunks by serial `importScripts`, and an
11
+ * import there delayed the first data request 360ms at 80ms RTT.
12
+ */
13
+ export default class CoreLoadTrackCode extends RpcMethodType<'CoreLoadTrackCode'> {
14
+ name: 'CoreLoadTrackCode';
15
+ execute({ adapterTypes, rpcMethods, }: RpcExecuteArgs<'CoreLoadTrackCode'>): Promise<void>;
16
+ serializeArguments(args: Record<string, unknown>): Promise<Record<string, unknown>>;
17
+ }
@@ -0,0 +1,24 @@
1
+ import RpcMethodType from '../../pluggableElementTypes/RpcMethodType.js';
2
+ /**
3
+ * Load the code a track's first request runs in the worker it is about to use:
4
+ * its adapter classes and the RPC methods its display names. Each arrives by
5
+ * dynamic import on that request otherwise, after the view lays out, the
6
+ * assembly loads and the request's refNames resolve.
7
+ *
8
+ * At launch rather than at worker boot: until `parallelizeChunkLoading` is
9
+ * installed a classic worker loads chunks by serial `importScripts`, and an
10
+ * import there delayed the first data request 360ms at 80ms RTT.
11
+ */
12
+ export default class CoreLoadTrackCode extends RpcMethodType {
13
+ name = 'CoreLoadTrackCode';
14
+ async execute({ adapterTypes, rpcMethods, }) {
15
+ const { pluginManager } = this;
16
+ await Promise.all([
17
+ ...adapterTypes.map(type => pluginManager.getAdapterType(type).getAdapterClass()),
18
+ ...rpcMethods.map(name => pluginManager.getRpcMethodType(name).preload()),
19
+ ]);
20
+ }
21
+ async serializeArguments(args) {
22
+ return args;
23
+ }
24
+ }
package/esm/ui/AppLogo.js CHANGED
@@ -1,10 +1,50 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
2
3
  import { observer } from 'mobx-react';
3
4
  import { readConfObject } from '../configuration/index.js';
5
+ import { isUriLocation } from '../util/types/data.js';
4
6
  import { LogoFull } from './Logo.js';
7
+ function resolvedUri(location) {
8
+ return location.baseUri
9
+ ? new URL(location.uri, location.baseUri).href
10
+ : location.uri;
11
+ }
12
+ // A URI goes straight to the <img>, which needs no CORS; a local path, a
13
+ // blob or a file handle is read into an object URL.
14
+ function useLogoSrc(location) {
15
+ const [objectUrl, setObjectUrl] = useState();
16
+ useEffect(() => {
17
+ if (!location || isUriLocation(location)) {
18
+ return;
19
+ }
20
+ let url;
21
+ let cancelled = false;
22
+ import('../util/io/index.js')
23
+ .then(({ openLocation }) => openLocation(location).readFile())
24
+ .then(bytes => {
25
+ if (!cancelled) {
26
+ url = URL.createObjectURL(new Blob([bytes]));
27
+ setObjectUrl(url);
28
+ }
29
+ })
30
+ .catch((e) => {
31
+ console.error(e);
32
+ });
33
+ return () => {
34
+ cancelled = true;
35
+ if (url) {
36
+ URL.revokeObjectURL(url);
37
+ }
38
+ };
39
+ }, [location]);
40
+ return location && isUriLocation(location)
41
+ ? resolvedUri(location)
42
+ : location
43
+ ? objectUrl
44
+ : undefined;
45
+ }
5
46
  const Logo = observer(function Logo({ session, }) {
6
- const { configuration } = session;
7
- const logoPath = readConfObject(configuration, 'logoPath');
8
- return logoPath?.uri ? (_jsx("img", { src: logoPath.uri, alt: "Custom logo" })) : (_jsx(LogoFull, { variant: "white" }));
47
+ const src = useLogoSrc(readConfObject(session.configuration, 'logoPath'));
48
+ return src ? (_jsx("img", { src: src, alt: "Custom logo" })) : (_jsx(LogoFull, { variant: "white" }));
9
49
  });
10
50
  export default Logo;
@@ -7,7 +7,7 @@ export interface JexlFilterExample {
7
7
  }
8
8
  /**
9
9
  * Editor for a display's jexl feature filters, for any display implementing
10
- * the two-tier {@link JexlFilterModel} contract. The Conditions tab shows each
10
+ * the {@link JexlFilterModel} contract. The Conditions tab shows each
11
11
  * line as rows of field, operator and value where it can, and as text where it
12
12
  * cannot; the Text tab is every line as text.
13
13
  *
@@ -6,7 +6,6 @@ import { Button, ListItemText, MenuItem, Tab, Tabs, TextField, } from '@mui/mate
6
6
  import { observer } from 'mobx-react';
7
7
  import { readConfObject } from '../configuration/readConfObject.js';
8
8
  import { addRow, describedColumns, readFilterRows, removeRow, replaceRow, resolveFields, stripJexlPrefix, writeFilterRows, } from '../util/jexlFilterRows.js';
9
- import { activeJexlFilters } from '../util/jexlFilters.js';
10
9
  import { ensureJexlPrefix } from '../util/jexlStrings.js';
11
10
  import { getContainingTrack } from '../util/mstUtils.js';
12
11
  import { makeStyles } from '../util/tss-react/index.js';
@@ -100,7 +99,7 @@ function withOneRow(state) {
100
99
  }
101
100
  /**
102
101
  * Editor for a display's jexl feature filters, for any display implementing
103
- * the two-tier {@link JexlFilterModel} contract. The Conditions tab shows each
102
+ * the {@link JexlFilterModel} contract. The Conditions tab shows each
104
103
  * line as rows of field, operator and value where it can, and as text where it
105
104
  * cannot; the Text tab is every line as text.
106
105
  *
@@ -116,7 +115,7 @@ const JexlFilterDialog = observer(function JexlFilterDialog({ model, handleClose
116
115
  const fieldList = fields instanceof Promise ? use(fields) : fields;
117
116
  const described = metadata && use(metadata);
118
117
  const [choices] = useState(() => resolveFields([...fieldList, ...describedColumns(described, fieldList)]));
119
- const [state, setState] = useState(() => withOneRow(readFilterRows(activeJexlFilters(model), jexl, choices)));
118
+ const [state, setState] = useState(() => withOneRow(readFilterRows(model.configuredFilters(), jexl, choices)));
120
119
  const [tab, setTab] = useState('conditions');
121
120
  const [text, setText] = useState({ value: '', shown: '' });
122
121
  const textEdited = text.value !== text.shown;
@@ -1,10 +1,10 @@
1
1
  import type PluginManager from '../PluginManager.ts';
2
- import type { AnyConfigurationModel } from '../configuration/index.ts';
2
+ import type { AnyTrackConfig } from '../configuration/index.ts';
3
3
  import type { AbstractSessionModel, TrackActionView } from '../util/types/index.ts';
4
4
  import type { MenuItem } from './MenuTypes.ts';
5
5
  export interface ExtraTrackMenuItemsProps {
6
6
  session: AbstractSessionModel;
7
- config: AnyConfigurationModel;
7
+ config: AnyTrackConfig;
8
8
  view?: TrackActionView;
9
9
  }
10
10
  /** Return the items to add, or nothing when the track isn't yours. */
@@ -15,11 +15,9 @@ export declare const paletteColors: {
15
15
  * The wide qualitative palette: every scheme above, in order, deduped and with
16
16
  * the neutrals dropped. ~40 entries.
17
17
  *
18
- * One list, because "give each row a distinct color" is one question this repo
19
- * answers in two places — the multi-row painter handing colors out by row
20
- * index, and the arrangement dialog's palette-by-attribute — and two lists
21
- * would mean a track's automatic colors and the colors it takes when a user
22
- * palettes it by hand were different palettes for no reason a reader could see.
18
+ * A feature's categorical colour deals from it; a row's deals from
19
+ * `rowPaletteColorAt`, which re-lights tableau10 rather than reaching for
20
+ * another scheme's hues.
23
21
  *
24
22
  * `tableau10` leads because its hues are the most evenly separated, so a track
25
23
  * with a handful of rows spends only that. The tail is what makes the 26
@@ -31,21 +29,16 @@ export declare const paletteColors: {
31
29
  * re-palettes), while a by-position painter has no value to hash and wraps.
32
30
  *
33
31
  * **It holds 14 pairs `similarColors` calls the same** — five schemes' blues,
34
- * oranges, greens, reds and pinks — and deduping it at construction is the
35
- * wrong reading of that, measured on the 38 GENCODE gene biotypes. Hashed,
36
- * which is what a track with no `colorDomain` does, 30 of them land on 22
37
- * colors and 38 on 26; the loss is the birthday collision on 40 slots, not the
38
- * near-twins, and only 3 of the pairs they land on read alike. Pruning to the
39
- * 30 distinct entries makes that 23 and 24, so it costs ten colors and buys
40
- * nothing.
32
+ * oranges, greens, reds and pinks. A dealt scale (`categoricalScale`'s `held`)
33
+ * steps over a twin of a color already dealt, so they cost a track only once
34
+ * it has more values than the 30 distinct entries.
41
35
  *
42
- * The near-twins bite the PINNED case instead, where a `domain` spends the
43
- * list in order: 30 listed values take 30 colors today and 7 of those pairs
44
- * read alike. Ordering the distinct 30 first and the twins behind them fixes
45
- * that outright (0 pairs at 30 listed values, 11 at 38 where the twins are
46
- * reached anyway) and costs no colors — but it moves every hashed color in
47
- * marks, GWAS, canvas, multi-row and the arrangement dialog, so it is a pass
48
- * with a figure refresh in it.
36
+ * The near-twins bite a `domain` instead, which spends the list in order: 30
37
+ * listed values take 30 colors and 7 of those pairs read alike. Ordering the
38
+ * distinct 30 first and the twins behind them fixes that outright (0 pairs at
39
+ * 30 listed values, 11 at 38 where the twins are reached anyway) and costs no
40
+ * colors — but it moves every hashed color in marks, GWAS, canvas, multi-row
41
+ * and the arrangement dialog, so it is a pass with a figure refresh in it.
49
42
  */
50
43
  export declare const categoricalPalette: string[];
51
44
  /**
@@ -60,18 +53,30 @@ export declare const categoricalPalette: string[];
60
53
  export declare function categoricalValueColor(value: string): string;
61
54
  export declare function categoricalValueColor<T>(value: string, palette: readonly T[]): T;
62
55
  /**
63
- * The categorical rule every scaled channel resolves through. With no
64
- * `domain` a value takes `categoricalValueColor`'s slot in `range`. With one,
65
- * the listed values take `range` in order, continuing into the `fallback`
66
- * entries `range` lacks past its end, and any other value takes a slot of the
67
- * whole list derived from itself that no listed value holds, nor one
68
- * `isNear` calls the same. A value's entry depends only on the value and the
69
- * declaration, so every region agrees on it, and adding a listed value moves
70
- * only the unlisted values on the slots it takes.
56
+ * The slots a categorical scale has dealt the values it met that its `domain`
57
+ * does not list, each recorded on first sight and never dealt again. The
58
+ * caller owns it and hands the same one to every scale that should agree.
71
59
  */
72
- export declare function categoricalScale<T>(domain: readonly (string | number)[] | undefined, range: readonly T[], { fallback, isNear, }?: {
60
+ export type HeldSlots = Map<string, number>;
61
+ /**
62
+ * The categorical rule every scaled channel resolves through. The values
63
+ * `domain` lists take `range` in order, continuing into the `fallback`
64
+ * entries `range` lacks past its end. Any other value takes the slot derived
65
+ * from itself (`categoricalValueColor`'s, with no `domain`) unless a listed
66
+ * value holds it or one `isNear` calls the same, and then another slot derived
67
+ * from itself.
68
+ *
69
+ * With `held`, a value is dealt once, on first sight: its slot avoids every
70
+ * slot `held` already records and their near twins as well, so values met
71
+ * together never share an entry while the list has room, and the slot is
72
+ * recorded so the value keeps it as others arrive. A value nothing else
73
+ * collides with keeps the entry it takes without `held`. With no `domain`, the
74
+ * deal spends `range` before spilling into the `fallback` entries it lacks.
75
+ */
76
+ export declare function categoricalScale<T>(domain: readonly (string | number)[] | undefined, range: readonly T[], { fallback, isNear, held, }?: {
73
77
  fallback?: readonly T[];
74
78
  isNear?: (a: T, b: T) => boolean;
79
+ held?: HeldSlots;
75
80
  }): (value: string) => T;
76
81
  /**
77
82
  * Whether two CSS colors read as one: under 0.05 apart in OKLab, where the
@@ -81,9 +86,10 @@ export declare function similarColors(a: string, b: string): boolean;
81
86
  /**
82
87
  * `categoricalScale` for a color channel: `palette` (the wide palette when
83
88
  * empty) spent in `domain` order and continued into the wide palette, and an
84
- * unlisted value never on a listed value's color or one that reads the same.
89
+ * unlisted value never on a listed value's color or one that reads the same,
90
+ * nor, with `held`, on the color of a value met before it.
85
91
  */
86
- export declare function categoricalColorScale(domain: readonly (string | number)[] | undefined, palette?: readonly string[]): (value: string) => string;
92
+ export declare function categoricalColorScale(domain: readonly (string | number)[] | undefined, palette?: readonly string[], held?: HeldSlots): (value: string) => string;
87
93
  /**
88
94
  * The color one value of a categorical color channel paints, by
89
95
  * `categoricalColorScale`. A value-less feature is neutral grey, and an array
@@ -104,6 +110,11 @@ export declare function hashString(str: string): number;
104
110
  export declare function getQueryColor(queryName: string): string;
105
111
  /** The palette color for a chromosome at `position` in its assembly. */
106
112
  export declare function refNamePaletteColorAt(position: number): string;
113
+ /**
114
+ * The colour the row palette deals the row or value at `position`: tableau10
115
+ * less its grey, then re-lit laps of it, as a karyotype's are.
116
+ */
117
+ export declare function rowPaletteColorAt(position: number): string;
107
118
  /**
108
119
  * The color a refName paints, BY POSITION IN THE ASSEMBLY where the caller
109
120
  * knows its chromosome order.