@jbrowse/core 5.0.0-beta.4 → 5.0.0-beta.7

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 (110) hide show
  1. package/README.md +278 -250
  2. package/esm/PluginManager.js +1 -1
  3. package/esm/ReExports/publicUi.d.ts +1 -1
  4. package/esm/ReExports/publicUi.js +1 -1
  5. package/esm/ReExports/publicUtil.d.ts +1 -1
  6. package/esm/ReExports/publicUtil.js +1 -1
  7. package/esm/ReExports/workerNamespaceNames.js +3 -9
  8. package/esm/assemblyManager/assemblyConfigSchema.d.ts +2 -1
  9. package/esm/assemblyManager/assemblyConfigSchema.js +2 -1
  10. package/esm/assemblyManager/expandAssemblyConfigShorthand.d.ts +15 -15
  11. package/esm/assemblyManager/expandAssemblyConfigShorthand.js +34 -18
  12. package/esm/configuration/configurationSchema.d.ts +1 -2
  13. package/esm/configuration/configurationSchema.js +3 -8
  14. package/esm/configuration/configurationSlot.d.ts +0 -47
  15. package/esm/configuration/configurationSlot.js +4 -54
  16. package/esm/configuration/fillLocations.d.ts +14 -0
  17. package/esm/configuration/fillLocations.js +14 -0
  18. package/esm/configuration/fullConfSnapshot.d.ts +0 -15
  19. package/esm/configuration/fullConfSnapshot.js +0 -28
  20. package/esm/configuration/getConf.d.ts +4 -34
  21. package/esm/configuration/getConf.js +3 -38
  22. package/esm/configuration/index.d.ts +3 -4
  23. package/esm/configuration/index.js +3 -2
  24. package/esm/configuration/schemaRegistry.d.ts +1 -2
  25. package/esm/configuration/schemaRegistry.js +1 -2
  26. package/esm/configuration/slotFacade.d.ts +1 -16
  27. package/esm/configuration/slotFacade.js +1 -2
  28. package/esm/configuration/tabixShorthand.js +3 -3
  29. package/esm/configuration/types.d.ts +4 -16
  30. package/esm/data_adapters/CytobandAdapter/configSchema.js +3 -4
  31. package/esm/pluggableElementTypes/DisplayType.d.ts +5 -3
  32. package/esm/pluggableElementTypes/DisplayType.js +3 -1
  33. package/esm/pluggableElementTypes/models/BaseTrackModel.d.ts +30 -0
  34. package/esm/pluggableElementTypes/models/BaseTrackModel.js +45 -17
  35. package/esm/rpc/RpcRegistry.d.ts +7 -1
  36. package/esm/rpc/coreRpcMethods.d.ts +1 -0
  37. package/esm/rpc/coreRpcMethods.js +1 -0
  38. package/esm/rpc/methods/CoreEncodeFeatures.d.ts +15 -0
  39. package/esm/rpc/methods/CoreEncodeFeatures.js +62 -0
  40. package/esm/svg/svgId.d.ts +1 -1
  41. package/esm/svg/svgId.js +1 -1
  42. package/esm/ui/LegendSwatchGlyph.js +26 -21
  43. package/esm/ui/MenuTypes.d.ts +5 -52
  44. package/esm/ui/SizeSliderRow.d.ts +1 -3
  45. package/esm/ui/SizeSliderRow.js +4 -6
  46. package/esm/ui/SvgColorLegend.d.ts +9 -3
  47. package/esm/ui/SvgColorLegend.js +74 -33
  48. package/esm/ui/colorScale.d.ts +64 -0
  49. package/esm/ui/colorScale.js +41 -0
  50. package/esm/ui/colors.d.ts +11 -0
  51. package/esm/ui/colors.js +7 -0
  52. package/esm/ui/index.d.ts +5 -7
  53. package/esm/ui/index.js +3 -13
  54. package/esm/ui/legendMenuItem.d.ts +5 -16
  55. package/esm/ui/legendMenuItem.js +3 -14
  56. package/esm/ui/legendSpec.d.ts +11 -5
  57. package/esm/ui/legendSpec.js +8 -11
  58. package/esm/ui/makeSizeMenu.d.ts +0 -6
  59. package/esm/ui/makeSizeMenu.js +6 -30
  60. package/esm/ui/measureLegendText.js +1 -1
  61. package/esm/ui/menuItemAdornment.d.ts +9 -19
  62. package/esm/ui/menuItemAdornment.js +10 -31
  63. package/esm/ui/menuItems.d.ts +2 -6
  64. package/esm/ui/menuItems.js +0 -4
  65. package/esm/ui/palette.js +2 -1
  66. package/esm/ui/toggleMenuItems.d.ts +6 -13
  67. package/esm/ui/toggleMenuItems.js +13 -29
  68. package/esm/util/colorRamp.d.ts +3 -3
  69. package/esm/util/colorRamp.js +2 -2
  70. package/esm/util/featureTransforms.d.ts +15 -0
  71. package/esm/util/featureTransforms.js +342 -0
  72. package/esm/util/freezeDeep.d.ts +8 -10
  73. package/esm/util/freezeDeep.js +8 -10
  74. package/esm/util/glyphNames.d.ts +8 -0
  75. package/esm/util/glyphNames.js +12 -0
  76. package/esm/util/index.d.ts +0 -1
  77. package/esm/util/index.js +0 -1
  78. package/esm/util/markEncoding.d.ts +76 -0
  79. package/esm/util/markEncoding.js +436 -0
  80. package/esm/util/markEncodingTypes.d.ts +331 -0
  81. package/esm/util/markEncodingTypes.js +8 -0
  82. package/esm/util/renderToStaticMarkup.js +1 -1
  83. package/esm/util/starFusion.d.ts +26 -0
  84. package/esm/util/starFusion.js +37 -0
  85. package/esm/util/trackConfigDelta.d.ts +4 -8
  86. package/esm/util/trackConfigDelta.js +2 -4
  87. package/esm/util/tracks.d.ts +7 -1
  88. package/esm/util/tracks.js +72 -7
  89. package/esm/util/types/index.d.ts +12 -4
  90. package/esm/util/whenViewSettled.d.ts +9 -0
  91. package/esm/util/whenViewSettled.js +12 -4
  92. package/package.json +13 -3
  93. package/esm/configuration/promotableDefaults.d.ts +0 -234
  94. package/esm/configuration/promotableDefaults.js +0 -395
  95. package/esm/configuration/promotablePin.d.ts +0 -65
  96. package/esm/configuration/promotablePin.js +0 -1
  97. package/esm/configuration/promotableResolve.d.ts +0 -108
  98. package/esm/configuration/promotableResolve.js +0 -117
  99. package/esm/configuration/promotableSlots.d.ts +0 -12
  100. package/esm/configuration/promotableSlots.js +0 -45
  101. package/esm/configuration/slotShape.d.ts +0 -12
  102. package/esm/configuration/slotShape.js +0 -84
  103. package/esm/ui/PinAdornment.d.ts +0 -5
  104. package/esm/ui/PinAdornment.js +0 -67
  105. package/esm/ui/SvgGradientLegend.d.ts +0 -26
  106. package/esm/ui/SvgGradientLegend.js +0 -26
  107. package/esm/ui/promotablePinCoverage.d.ts +0 -58
  108. package/esm/ui/promotablePinCoverage.js +0 -124
  109. package/esm/util/openDisplays.d.ts +0 -23
  110. package/esm/util/openDisplays.js +0 -79
@@ -1,5 +1,4 @@
1
- import type { ResolvableDisplay } from './promotableResolve.ts';
2
- import type { AnyConfigurationModel, AnyConfigurationSnapshot, ConfigurationSchemaForModel, ConfigurationSlotName, ConfigurationSlotValue, ConfigurationSlotValueResolved } from './types.ts';
1
+ import type { AnyConfigurationModel, AnyConfigurationSnapshot, ConfigurationSchemaForModel, ConfigurationSlotName, ConfigurationSlotValue } from './types.ts';
3
2
  /**
4
3
  * #api core/configuration
5
4
  * Reads a configuration value from a state model that has a `.configuration`
@@ -12,12 +11,6 @@ import type { AnyConfigurationModel, AnyConfigurationSnapshot, ConfigurationSche
12
11
  * It does not consult the session and has no per-slot behavior; what you read is
13
12
  * what the track stores.
14
13
  *
15
- * A `promotable` slot read this way therefore yields the raw stored value,
16
- * `undefined` included — that `undefined` is the cascade's inherit sentinel, and
17
- * `resolveConf` is what turns it into a real value. The read type keeps the
18
- * `undefined` on purpose, so reaching for the wrong reader is a compile error
19
- * rather than a silent one.
20
- *
21
14
  * @param model - object containing a 'configuration' member
22
15
  * @param slotPaths - array of paths to read
23
16
  * @param args - extra arguments e.g. for a feature callback,
@@ -29,29 +22,6 @@ export declare function getConf(model: {
29
22
  export declare function getConf<CONFMODEL extends AnyConfigurationModel, SLOT extends ConfigurationSlotName<ConfigurationSchemaForModel<CONFMODEL>> | string[] = ConfigurationSlotName<ConfigurationSchemaForModel<CONFMODEL>>>(model: {
30
23
  configuration: CONFMODEL;
31
24
  }, slotPath: SLOT, args?: Record<string, unknown>): SLOT extends string ? ConfigurationSlotValue<ConfigurationSchemaForModel<CONFMODEL>, SLOT> : any;
32
- /**
33
- * #api core/configuration
34
- * Reads a `promotable` slot through the display-type-default cascade — the
35
- * track's own value, else the session-wide promoted default for this display
36
- * type, else the slot's `promotedBase`. Always yields a real value, never the
37
- * `undefined` inherit sentinel, so a display's value getter is
38
- * `get displayMode(): DisplayMode { return resolveConf(self, 'displayMode') }`
39
- * with no post-guard and no cast.
40
- *
41
- * Separate from `getConf` rather than folded into it, deliberately: resolution
42
- * consults the session, so it is main-thread only and throws on a detached node.
43
- * Folding it in was built and reverted — ADR-046.
44
- *
45
- * Throws if `slot` isn't promotable — the cascade has nothing to say about a
46
- * plain slot, and `getConf` is what you want there.
47
- *
48
- * Takes no jexl `args`, unlike `getConf`: a promotable slot cannot hold a
49
- * callback (see `SlotResolution`), so there is no per-feature context to supply.
50
- *
51
- * @param model - the display state model (needs the session + display type)
52
- * @param slot - the promotable slot to resolve
53
- */
54
- export declare function resolveConf<CONFMODEL extends AnyConfigurationModel, SLOT extends ConfigurationSlotName<ConfigurationSchemaForModel<CONFMODEL>> = ConfigurationSlotName<ConfigurationSchemaForModel<CONFMODEL>>>(model: ResolvableDisplay<CONFMODEL>, slot: SLOT): ConfigurationSlotValueResolved<ConfigurationSchemaForModel<CONFMODEL>, SLOT>;
55
25
  /**
56
26
  * #api core/configuration
57
27
  * Write counterpart to `getConf`: sets a slot on a state model that has a
@@ -75,9 +45,9 @@ export declare function resolveConf<CONFMODEL extends AnyConfigurationModel, SLO
75
45
  * `HostChecksSlotNames` pins each one.
76
46
  *
77
47
  * A wrong *value* type still throws at runtime (MST type-checks the assignment)
78
- * rather than at compile time. `value` is deliberately `unknown` because the
79
- * inherit sentinel (`undefined`/`null`) is a legitimate write on every
80
- * promotable slot, which the declared slot value type doesn't include.
48
+ * rather than at compile time. `value` is deliberately `unknown` because
49
+ * `undefined` is a legitimate write — it resets a slot to its schema default —
50
+ * and the declared slot value type doesn't include it.
81
51
  *
82
52
  * @param model - object containing a 'configuration' member
83
53
  * @param slotName - the slot to write
@@ -1,42 +1,7 @@
1
- import { resolveSlot } from './promotableResolve.js';
2
1
  import { readConfObject } from './readConfObject.js';
3
2
  export function getConf(model, slotPath, args = {}) {
4
3
  return readConfObject(model.configuration, slotPath, args);
5
4
  }
6
- /**
7
- * #api core/configuration
8
- * Reads a `promotable` slot through the display-type-default cascade — the
9
- * track's own value, else the session-wide promoted default for this display
10
- * type, else the slot's `promotedBase`. Always yields a real value, never the
11
- * `undefined` inherit sentinel, so a display's value getter is
12
- * `get displayMode(): DisplayMode { return resolveConf(self, 'displayMode') }`
13
- * with no post-guard and no cast.
14
- *
15
- * Separate from `getConf` rather than folded into it, deliberately: resolution
16
- * consults the session, so it is main-thread only and throws on a detached node.
17
- * Folding it in was built and reverted — ADR-046.
18
- *
19
- * Throws if `slot` isn't promotable — the cascade has nothing to say about a
20
- * plain slot, and `getConf` is what you want there.
21
- *
22
- * Takes no jexl `args`, unlike `getConf`: a promotable slot cannot hold a
23
- * callback (see `SlotResolution`), so there is no per-feature context to supply.
24
- *
25
- * @param model - the display state model (needs the session + display type)
26
- * @param slot - the promotable slot to resolve
27
- */
28
- export function resolveConf(
29
- // the display state node itself, not just its `.configuration`: the cascade
30
- // keys the session-wide tier on `type` and reaches the session through the
31
- // node. Asking for that shape is what keeps this cast-free — hand it a bare
32
- // config holder and tsc names the missing members instead of failing at the
33
- // first read. `ResolvableDisplay<CONFMODEL>`, never
34
- // `ResolvableDisplay & { configuration: CONFMODEL }`: the intersection
35
- // re-widens the slot names to `string` (see `ResolvableDisplay`)
36
- model, slot) {
37
- // the resolution is `unknown`, which the declared return type can't infer
38
- return resolveSlot(model, slot).value;
39
- }
40
5
  /**
41
6
  * #api core/configuration
42
7
  * Write counterpart to `getConf`: sets a slot on a state model that has a
@@ -60,9 +25,9 @@ model, slot) {
60
25
  * `HostChecksSlotNames` pins each one.
61
26
  *
62
27
  * A wrong *value* type still throws at runtime (MST type-checks the assignment)
63
- * rather than at compile time. `value` is deliberately `unknown` because the
64
- * inherit sentinel (`undefined`/`null`) is a legitimate write on every
65
- * promotable slot, which the declared slot value type doesn't include.
28
+ * rather than at compile time. `value` is deliberately `unknown` because
29
+ * `undefined` is a legitimate write — it resets a slot to its schema default —
30
+ * and the declared slot value type doesn't include it.
66
31
  *
67
32
  * @param model - object containing a 'configuration' member
68
33
  * @param slotName - the slot to write
@@ -7,13 +7,12 @@ export { FormatAboutConfigSchemaFactory } from './formatAboutConfigSchema.ts';
7
7
  export { DEFAULT_FORMAT_DETAILS_DEPTH, FormatDetailsConfigSchemaFactory, } from './formatDetailsConfigSchema.ts';
8
8
  export { mergeFormatCallbacks } from './mergeFormatCallbacks.ts';
9
9
  export { readConfObject, readConfSlot, readConfigValue, } from './readConfObject.ts';
10
- export { getConf, resolveConf, setConf } from './getConf.ts';
10
+ export { getConf, setConf } from './getConf.ts';
11
+ export { fillLocations } from './fillLocations.ts';
11
12
  export { expandTabixShorthand, tabixIndexSnapshot } from './tabixShorthand.ts';
12
13
  export { tabixIndexFields } from './tabixIndexFields.ts';
13
14
  export { evaluateJexl, isCallbackValue } from './slotValueUtils.ts';
14
15
  export { toCallbackValue, toFixedValue } from './configurationSlot.ts';
15
16
  export { getSlotDefinition, isConfigurationSlot, makeSlotFacade, preProcessSlotValues, slotChoices, } from './slotFacade.ts';
16
17
  export type { SlotFacade } from './slotFacade.ts';
17
- export type { ResolvableDisplay } from './promotableResolve.ts';
18
- export { clearPromotedDefaults, getDisplayTypeDefaultChanges, isSlotCustomized, makePin, makeTogglePin, getConfigSnapshotWithPromotables, getTrackConfigWithPromotables, } from './promotableDefaults.ts';
19
- export type { Pin, ResolvedConfigSnapshot, TogglePin, TrackConfigWithPromotables, ValuePin, } from './promotableDefaults.ts';
18
+ export { fullConfSnapshot } from './fullConfSnapshot.ts';
@@ -10,10 +10,11 @@ export { FormatAboutConfigSchemaFactory } from './formatAboutConfigSchema.js';
10
10
  export { DEFAULT_FORMAT_DETAILS_DEPTH, FormatDetailsConfigSchemaFactory, } from './formatDetailsConfigSchema.js';
11
11
  export { mergeFormatCallbacks } from './mergeFormatCallbacks.js';
12
12
  export { readConfObject, readConfSlot, readConfigValue, } from './readConfObject.js';
13
- export { getConf, resolveConf, setConf } from './getConf.js';
13
+ export { getConf, setConf } from './getConf.js';
14
+ export { fillLocations } from './fillLocations.js';
14
15
  export { expandTabixShorthand, tabixIndexSnapshot } from './tabixShorthand.js';
15
16
  export { tabixIndexFields } from './tabixIndexFields.js';
16
17
  export { evaluateJexl, isCallbackValue } from './slotValueUtils.js';
17
18
  export { toCallbackValue, toFixedValue } from './configurationSlot.js';
18
19
  export { getSlotDefinition, isConfigurationSlot, makeSlotFacade, preProcessSlotValues, slotChoices, } from './slotFacade.js';
19
- export { clearPromotedDefaults, getDisplayTypeDefaultChanges, isSlotCustomized, makePin, makeTogglePin, getConfigSnapshotWithPromotables, getTrackConfigWithPromotables, } from './promotableDefaults.js';
20
+ export { fullConfSnapshot } from './fullConfSnapshot.js';
@@ -17,8 +17,7 @@ export declare function isRegisteredConfigurationSchema(type: IAnyType): boolean
17
17
  * The slot/sub-schema/constant table for a config (includes slots merged in from
18
18
  * `baseConfiguration` at schema construction). Undefined when the argument isn't
19
19
  * a registered configuration schema. The single accessor for "what are this
20
- * config's slots?" — shared by the slot facade, promotable defaults, and
21
- * `fullConfSnapshot`.
20
+ * config's slots?" — shared by the slot facade and `fullConfSnapshot`.
22
21
  *
23
22
  * Takes a live node *or* the schema type itself. Every in-tree reader holds a
24
23
  * node, which is why the `getType` hop is here; a caller enumerating registered
@@ -28,8 +28,7 @@ export function isRegisteredConfigurationSchema(type) {
28
28
  * The slot/sub-schema/constant table for a config (includes slots merged in from
29
29
  * `baseConfiguration` at schema construction). Undefined when the argument isn't
30
30
  * a registered configuration schema. The single accessor for "what are this
31
- * config's slots?" — shared by the slot facade, promotable defaults, and
32
- * `fullConfSnapshot`.
31
+ * config's slots?" — shared by the slot facade and `fullConfSnapshot`.
33
32
  *
34
33
  * Takes a live node *or* the schema type itself. Every in-tree reader holds a
35
34
  * node, which is why the `getType` hop is here; a caller enumerating registered
@@ -32,20 +32,6 @@ export interface SlotFacade {
32
32
  type: ConfigSlotType;
33
33
  contextVariable: string[];
34
34
  defaultValue: unknown;
35
- /**
36
- * For a `promotable` slot, the value its unset state resolves to when nothing
37
- * is promoted (see `promotableResolve.ts`). Undefined on a plain slot, whose
38
- * `defaultValue` is already the concrete value.
39
- *
40
- * Here because a promotable slot's `defaultValue` is always the `undefined`
41
- * inherit sentinel, so an editor rendering `defaultValue` for an unset slot
42
- * shows nothing at all — for a `maybeBoolean` that reads as a concrete
43
- * `false`, which is wrong for every slot whose base is `true`. The editor
44
- * still cannot see the *session* tier of the cascade (its target is often a
45
- * detached config node), so this is the honest answer it can give: what unset
46
- * means with nothing promoted.
47
- */
48
- promotedBase?: unknown;
49
35
  /** enum choices, present only for `stringEnum`/`maybeStringEnum` slots */
50
36
  choices?: string[];
51
37
  pluginManager: PluginManager;
@@ -54,8 +40,7 @@ export interface SlotFacade {
54
40
  * Whether the slot holds a non-default value. Each slot is `stripDefault`-
55
41
  * wrapped, so an at-default slot is omitted from the config snapshot — the
56
42
  * same signal the persistence/delta layer uses (robust for map/array slots
57
- * where a live-value `deepEqual` against the plain default would misfire). For
58
- * a promotable slot, "non-default" means customized rather than inheriting.
43
+ * where a live-value `deepEqual` against the plain default would misfire).
59
44
  */
60
45
  readonly modified: boolean;
61
46
  set: (val: unknown) => void;
@@ -66,14 +66,13 @@ export function getSlotDefinition(node, slotName) {
66
66
  }
67
67
  export function makeSlotFacade(node, slotName) {
68
68
  const definition = getSlotDefinition(node, slotName);
69
- const { type, description = '', defaultValue, promotedBase, contextVariable = [], } = definition;
69
+ const { type, description = '', defaultValue, contextVariable = [], } = definition;
70
70
  return {
71
71
  name: slotName,
72
72
  description,
73
73
  type,
74
74
  contextVariable,
75
75
  defaultValue,
76
- promotedBase,
77
76
  choices: slotChoices(definition),
78
77
  pluginManager: getEnv(node).pluginManager,
79
78
  get value() {
@@ -1,3 +1,4 @@
1
+ import { fillLocations } from './fillLocations.js';
1
2
  /**
2
3
  * The `index` half of a tabix-indexed adapter's shorthand snapshot: given
3
4
  * `{ uri, baseUri?, csi? }`, where the index file sits and what kind it is.
@@ -36,10 +37,9 @@ export function tabixIndexSnapshot(snap) {
36
37
  */
37
38
  export function expandTabixShorthand(snap, locationKey) {
38
39
  return snap.uri
39
- ? {
40
- ...snap,
40
+ ? fillLocations(snap, {
41
41
  [locationKey]: { uri: snap.uri, baseUri: snap.baseUri },
42
42
  index: tabixIndexSnapshot(snap),
43
- }
43
+ })
44
44
  : snap;
45
45
  }
@@ -23,7 +23,7 @@ export type ConfigurationSlotName<SCHEMA> = SCHEMA extends undefined ? never : S
23
23
  * every value left `unknown`.
24
24
  *
25
25
  * Names are the whole point of it. A slot value can be its own type, a `jexl:`
26
- * callback string, or (for a promotable slot) absent, and nothing here tries to
26
+ * callback string, or (for a `maybe*` slot) absent, and nothing here tries to
27
27
  * check that; a *name* the schema does not declare is dropped on the way in
28
28
  * without a word, so `preferance` is a setting that reads as applied and never
29
29
  * applies. Against an object literal — which is how an embedder writes one —
@@ -34,11 +34,6 @@ export type ConfigurationSnapshot<SCHEMA> = SCHEMA extends undefined ? never : S
34
34
  } & {
35
35
  [K in GetExplicitIdentifier<SCHEMA>]?: string;
36
36
  } & (GetBase<SCHEMA> extends ConfigurationSchemaType<any, any> ? ConfigurationSnapshot<GetBase<SCHEMA>> : unknown) : unknown;
37
- type SlotValueResolvedFromDef<DEF> = DEF extends {
38
- promotedBase: undefined;
39
- } ? SlotValueRawFromDef<DEF> : DEF extends {
40
- promotedBase: unknown;
41
- } ? Exclude<SlotValueRawFromDef<DEF>, undefined> : SlotValueRawFromDef<DEF>;
42
37
  /**
43
38
  * The value each builtin slot `type` reads as — the type-level twin of
44
39
  * `slotTypes` in `configurationSlot.ts`, which is the same list as MST models.
@@ -66,9 +61,8 @@ interface SlotValueByType {
66
61
  * only add cast ceremony on values that are legitimately dynamic.
67
62
  *
68
63
  * Stated here rather than left to the `defaultValue` fallback, which reaches
69
- * `any` for these only by accident: it lands there because no `frozen` slot in
70
- * the repo happens to default to a scalar, and a promotable slot's default is
71
- * always the `undefined` sentinel.
64
+ * `any` for these only by accident: no `frozen` slot in the repo happens to
65
+ * default to a scalar.
72
66
  */
73
67
  frozen: any;
74
68
  maybeFrozen: any;
@@ -106,11 +100,6 @@ type SlotValueRawFromDef<DEF> = DEF extends AnyConfigurationSchemaType ? AnyConf
106
100
  } ? [V] extends [boolean] ? boolean : [V] extends [string] ? string : [V] extends [number] ? number : any : any;
107
101
  /** what a raw read (`getConf` / `readConfObject`) of this slot yields */
108
102
  export type ConfigurationSlotValue<SCHEMA, K extends string> = SCHEMA extends ConfigurationSchemaType<infer D, any> ? K extends keyof D ? SlotValueRawFromDef<D[K]> : GetBase<SCHEMA> extends ConfigurationSchemaType<any, any> ? ConfigurationSlotValue<GetBase<SCHEMA>, K> : any : any;
109
- /**
110
- * what `resolveConf` yields: the same, minus the inherit sentinel on a
111
- * promotable slot — the cascade always produces a real value.
112
- */
113
- export type ConfigurationSlotValueResolved<SCHEMA, K extends string> = SCHEMA extends ConfigurationSchemaType<infer D, any> ? K extends keyof D ? SlotValueResolvedFromDef<D[K]> : GetBase<SCHEMA> extends ConfigurationSchemaType<any, any> ? ConfigurationSlotValueResolved<GetBase<SCHEMA>, K> : any : any;
114
103
  /**
115
104
  * Naming convention for config types, paired per schema:
116
105
  * - `XConfigSchema` is the MST IType (the schema itself). Use it for
@@ -154,8 +143,7 @@ export type ConfigModelForFields<FIELDS extends ConfigurationSchemaDefinition, B
154
143
  * ```
155
144
  *
156
145
  * Worth a named type because the widened form has **no symptom** — a mixin host
157
- * cast to `AnyConfigurationModel`, or to the `ResolvableDisplay & { … }`
158
- * intersection that re-widens, compiles and runs and checks nothing. Every
146
+ * cast to `AnyConfigurationModel` compiles and runs and checks nothing. Every
159
147
  * misspelled slot name below it typechecks, and a misspelled *read* returns
160
148
  * `undefined` with no diagnostic at any layer, so only a sabotage or this
161
149
  * assertion tells the two apart.
@@ -1,4 +1,4 @@
1
- import { ConfigurationSchema } from '../../configuration/index.js';
1
+ import { ConfigurationSchema, fillLocations, } from '../../configuration/index.js';
2
2
  /**
3
3
  * #config CytobandAdapter
4
4
  *
@@ -55,13 +55,12 @@ const configSchema = ConfigurationSchema('CytobandAdapter', {
55
55
  preProcessSnapshot: snap => {
56
56
  // populate from just snap.uri
57
57
  return snap.uri
58
- ? {
59
- ...snap,
58
+ ? fillLocations(snap, {
60
59
  cytobandLocation: {
61
60
  uri: snap.uri,
62
61
  baseUri: snap.baseUri,
63
62
  },
64
- }
63
+ })
65
64
  : snap;
66
65
  },
67
66
  });
@@ -7,9 +7,11 @@ export default class DisplayType extends LazyStateModelElement {
7
7
  configSchema: AnyConfigurationSchemaType;
8
8
  ReactComponent: AnyReactComponentType;
9
9
  /**
10
- * The track type the display is associated with
10
+ * The track type the display attaches to, or several: a display that reads
11
+ * a field any feature adapter serves (the Manhattan plot's `score`) belongs
12
+ * to every track type whose adapters serve it.
11
13
  */
12
- trackType: string;
14
+ trackType: string | string[];
13
15
  /**
14
16
  * The view type the display is associated with
15
17
  */
@@ -21,7 +23,7 @@ export default class DisplayType extends LazyStateModelElement {
21
23
  constructor(stuff: {
22
24
  name: string;
23
25
  stateModel: IAnyModelType | (() => Promise<IAnyModelType>);
24
- trackType: string;
26
+ trackType: string | string[];
25
27
  viewType: string;
26
28
  displayName?: string;
27
29
  configSchema: AnyConfigurationSchemaType;
@@ -4,7 +4,9 @@ export default class DisplayType extends LazyStateModelElement {
4
4
  configSchema;
5
5
  ReactComponent;
6
6
  /**
7
- * The track type the display is associated with
7
+ * The track type the display attaches to, or several: a display that reads
8
+ * a field any feature adapter serves (the Manhattan plot's `score`) belongs
9
+ * to every track type whose adapters serve it.
8
10
  */
9
11
  trackType;
10
12
  /**
@@ -95,6 +95,22 @@ export declare function createBaseTrackModel(pm: PluginManager, trackType: strin
95
95
  * a shown track always has at least one display
96
96
  */
97
97
  readonly activeDisplay: DisplayModel & import("@jbrowse/mobx-state-tree").IStateTreeNode<IType<unknown, unknown, DisplayModel>>;
98
+ /**
99
+ * #getter
100
+ * the configured displays the CONTAINING VIEW can draw, which is the set
101
+ * `replaceDisplay` will accept
102
+ *
103
+ * `configuration.displays` is not that set: baseTrackConfig injects one
104
+ * entry per display type registered for the track TYPE, across every
105
+ * view type, so a VariantTrack carries a ChordVariantDisplay a linear
106
+ * view cannot draw. The track menu's own "Display types" submenu has
107
+ * always filtered them; this exposes the same list to code, because the
108
+ * alternative is every caller re-deriving it from the plugin manager.
109
+ */
110
+ readonly compatibleDisplays: (import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
111
+ setSubschema(slotName: string, data: Record<string, unknown>): any;
112
+ setSlot(slotName: string, value: unknown): void;
113
+ } & import("@jbrowse/mobx-state-tree").IStateTreeNode<AnyConfigurationSchemaType>)[];
98
114
  /**
99
115
  * #getter
100
116
  */
@@ -195,6 +211,20 @@ export declare function createBaseTrackModel(pm: PluginManager, trackType: strin
195
211
  * stops changing.
196
212
  */
197
213
  afterAttach(): void;
214
+ } & {
215
+ /**
216
+ * #action
217
+ * The awaitable sibling of `replaceDisplay`: loads the target display's
218
+ * state model, then swaps to it carrying whatever settings the outgoing
219
+ * display can hand over.
220
+ *
221
+ * `replaceDisplay` stays a plain sync action and ASSERTS the model is
222
+ * loaded, and every display state model in this tree is a dynamic import
223
+ * until something shows one — so a caller that has not already shown the
224
+ * target needs this. Named like `launchTrack`, which is the same problem
225
+ * one level up.
226
+ */
227
+ launchDisplay(displayId: string): Promise<void>;
198
228
  } & {
199
229
  /**
200
230
  * #getter
@@ -156,6 +156,21 @@ export function createBaseTrackModel(pm, trackType, baseTrackConfig) {
156
156
  get activeDisplay() {
157
157
  return self.displays[0];
158
158
  },
159
+ /**
160
+ * #getter
161
+ * the configured displays the CONTAINING VIEW can draw, which is the set
162
+ * `replaceDisplay` will accept
163
+ *
164
+ * `configuration.displays` is not that set: baseTrackConfig injects one
165
+ * entry per display type registered for the track TYPE, across every
166
+ * view type, so a VariantTrack carries a ChordVariantDisplay a linear
167
+ * view cannot draw. The track menu's own "Display types" submenu has
168
+ * always filtered them; this exposes the same list to code, because the
169
+ * alternative is every caller re-deriving it from the plugin manager.
170
+ */
171
+ get compatibleDisplays() {
172
+ return getCompatibleDisplays(self);
173
+ },
159
174
  /**
160
175
  * #getter
161
176
  */
@@ -350,6 +365,28 @@ export function createBaseTrackModel(pm, trackType, baseTrackConfig) {
350
365
  void releaseAdapterSession(rpcManager, retained);
351
366
  });
352
367
  },
368
+ }))
369
+ .actions(self => ({
370
+ /**
371
+ * #action
372
+ * The awaitable sibling of `replaceDisplay`: loads the target display's
373
+ * state model, then swaps to it carrying whatever settings the outgoing
374
+ * display can hand over.
375
+ *
376
+ * `replaceDisplay` stays a plain sync action and ASSERTS the model is
377
+ * loaded, and every display state model in this tree is a dynamic import
378
+ * until something shows one — so a caller that has not already shown the
379
+ * target needs this. Named like `launchTrack`, which is the same problem
380
+ * one level up.
381
+ */
382
+ async launchDisplay(displayId) {
383
+ const displays = self.configuration.displays;
384
+ const { type } = getDisplayConf(displays, displayId);
385
+ await pm.resolveDisplayTypeRecord(type)?.loadStateModel();
386
+ if (isAlive(self)) {
387
+ self.replaceDisplay(self.activeDisplay.configuration.displayId, displayId, self.activeDisplay.getPortableSettings?.(displayId) ?? {});
388
+ }
389
+ },
353
390
  }))
354
391
  .views(self => ({
355
392
  /**
@@ -430,7 +467,7 @@ export function createBaseTrackModel(pm, trackType, baseTrackConfig) {
430
467
  trackMenuItems() {
431
468
  const menuItems = self.displays.flatMap(d => d.trackMenuItems());
432
469
  const shownId = self.activeDisplay.configuration.displayId;
433
- const compatDisp = getCompatibleDisplays(self);
470
+ const compatDisp = self.compatibleDisplays;
434
471
  return [
435
472
  ...menuItems,
436
473
  ...(compatDisp.length > 1 && shownId
@@ -451,24 +488,15 @@ export function createBaseTrackModel(pm, trackType, baseTrackConfig) {
451
488
  // being replaced, so leaving the menu up would keep a
452
489
  // list of items built against a destroyed MST node
453
490
  keepMenuOpen: false,
454
- // the chosen display's state model may still be a dynamic
455
- // import away, so load it before swapping — replaceDisplay
456
- // itself stays a plain sync action
491
+ // launchDisplay, because the chosen display's state
492
+ // model may still be a dynamic import away
457
493
  onClick: () => {
458
494
  if (d.displayId !== shownId) {
459
- void (async () => {
460
- try {
461
- await pm
462
- .resolveDisplayTypeRecord(d.type)
463
- ?.loadStateModel();
464
- if (isAlive(self)) {
465
- self.replaceDisplay(shownId, d.displayId, self.activeDisplay.getPortableSettings?.(d.displayId) ?? {});
466
- }
467
- }
468
- catch (e) {
469
- getSession(self).notifyError(`${e}`, e);
470
- }
471
- })();
495
+ void self
496
+ .launchDisplay(d.displayId)
497
+ .catch((e) => {
498
+ getSession(self).notifyError(`${e}`, e);
499
+ });
472
500
  }
473
501
  },
474
502
  };
@@ -1,11 +1,12 @@
1
1
  import type { FeatureDensity } from '../data_adapters/BaseAdapter/featureDensity.ts';
2
+ import type { CoreEncodeFeaturesArgs, EncodedFeaturesResult } from '../util/markEncodingTypes.ts';
2
3
  import type { StatusCallback } from '../util/progress.ts';
3
4
  import type { UnwrapRpcResult } from '../util/rpc.ts';
4
5
  import type { Feature, SimpleFeatureSerialized } from '../util/simpleFeature.ts';
5
6
  import type { StopToken } from '../util/stopToken.ts';
6
7
  import type { NoAssemblyRegion } from '../util/types/data.ts';
7
8
  import type { RpcResult } from './RpcServer.ts';
8
- import type { ByteEstimateScope } from './byteBudget.ts';
9
+ import type { ByteEstimateScope, RegionTooLargeResult } from './byteBudget.ts';
9
10
  export interface RegionLike {
10
11
  refName: string;
11
12
  start: number;
@@ -44,6 +45,11 @@ export interface RpcRegistry {
44
45
  return: Feature[];
45
46
  wire: SimpleFeatureSerialized[];
46
47
  };
48
+ CoreEncodeFeatures: {
49
+ args: CoreEncodeFeaturesArgs;
50
+ return: EncodedFeaturesResult | RegionTooLargeResult;
51
+ transferables: EncodedFeaturesResult;
52
+ };
47
53
  CoreGetRegionByteEstimate: {
48
54
  args: {
49
55
  adapterConfig: Record<string, unknown>;
@@ -2,6 +2,7 @@ export { default as CoreGetRefNames } from './methods/CoreGetRefNames.ts';
2
2
  export { default as CoreGetMetadata } from './methods/CoreGetMetadata.ts';
3
3
  export { default as CoreGetInfo } from './methods/CoreGetInfo.ts';
4
4
  export { default as CoreGetFeatures } from './methods/CoreGetFeatures.ts';
5
+ export { default as CoreEncodeFeatures } from './methods/CoreEncodeFeatures.ts';
5
6
  export { default as CoreGetSequence } from './methods/CoreGetSequence.ts';
6
7
  export { default as CoreFreeResources } from './methods/CoreFreeResources.ts';
7
8
  export { default as CoreGetRegionByteEstimate } from './methods/CoreGetRegionByteEstimate.ts';
@@ -2,6 +2,7 @@ export { default as CoreGetRefNames } from './methods/CoreGetRefNames.js';
2
2
  export { default as CoreGetMetadata } from './methods/CoreGetMetadata.js';
3
3
  export { default as CoreGetInfo } from './methods/CoreGetInfo.js';
4
4
  export { default as CoreGetFeatures } from './methods/CoreGetFeatures.js';
5
+ export { default as CoreEncodeFeatures } from './methods/CoreEncodeFeatures.js';
5
6
  export { default as CoreGetSequence } from './methods/CoreGetSequence.js';
6
7
  export { default as CoreFreeResources } from './methods/CoreFreeResources.js';
7
8
  export { default as CoreGetRegionByteEstimate } from './methods/CoreGetRegionByteEstimate.js';
@@ -0,0 +1,15 @@
1
+ import RpcMethodTypeWithRenameRegion from '../../pluggableElementTypes/RpcMethodTypeWithRenameRegion.ts';
2
+ import type { EncodedFeaturesResult } from '../../util/markEncoding.ts';
3
+ import type { RpcExecuteArgs } from '../RpcRegistry.ts';
4
+ /**
5
+ * Fetch a region's features once, run the shared transform steps over them,
6
+ * then each layer's own, and evaluate the layer's encoding over what is left
7
+ * in the worker, where the `Feature` objects are, filling the lanes its shape
8
+ * reads. The colours come back packed, the scale tables resolved, and the
9
+ * main thread reads the same table for its legend that the colours were
10
+ * drawn from.
11
+ */
12
+ export default class CoreEncodeFeatures extends RpcMethodTypeWithRenameRegion<'CoreEncodeFeatures'> {
13
+ name: 'CoreEncodeFeatures';
14
+ execute(args: RpcExecuteArgs<'CoreEncodeFeatures'>): Promise<import("../byteBudget.ts").RegionTooLargeResult | import("../RpcServer.ts").RpcResult<EncodedFeaturesResult>>;
15
+ }
@@ -0,0 +1,62 @@
1
+ import { getFeatureAdapterOrThrow } from '../../data_adapters/getFeatureAdapter.js';
2
+ import RpcMethodTypeWithRenameRegion from '../../pluggableElementTypes/RpcMethodTypeWithRenameRegion.js';
3
+ import { runTransforms } from '../../util/featureTransforms.js';
4
+ import { rpcResult } from '../../util/librpc.js';
5
+ import { encodeFeatures, encodedChannelTransferables, } from '../../util/markEncoding.js';
6
+ import { createProgressReporter, updateStatus } from '../../util/progress.js';
7
+ import { checkStopTokenThrottled, createStopTokenChecker, } from '../../util/stopToken.js';
8
+ import { measureRegionBytes } from '../byteBudget.js';
9
+ /**
10
+ * Fetch a region's features once, run the shared transform steps over them,
11
+ * then each layer's own, and evaluate the layer's encoding over what is left
12
+ * in the worker, where the `Feature` objects are, filling the lanes its shape
13
+ * reads. The colours come back packed, the scale tables resolved, and the
14
+ * main thread reads the same table for its legend that the colours were
15
+ * drawn from.
16
+ */
17
+ export default class CoreEncodeFeatures extends RpcMethodTypeWithRenameRegion {
18
+ name = 'CoreEncodeFeatures';
19
+ async execute(args) {
20
+ const { sessionId, adapterConfig, sequenceAdapter, region, layers: requested, transform = [], filters = [], byteLimit, stopToken, statusCallback, } = args;
21
+ const { pluginManager } = this;
22
+ const stopTokenCheck = createStopTokenChecker(stopToken);
23
+ const dataAdapter = await getFeatureAdapterOrThrow({
24
+ pluginManager,
25
+ sessionId,
26
+ adapterConfig,
27
+ sequenceAdapter,
28
+ });
29
+ const { bytes, tooLarge } = await measureRegionBytes({
30
+ dataAdapter,
31
+ regions: [region],
32
+ byteLimit,
33
+ stopToken,
34
+ statusCallback,
35
+ stopTokenCheck,
36
+ });
37
+ if (tooLarge) {
38
+ return tooLarge;
39
+ }
40
+ const fetched = await updateStatus('Downloading features', statusCallback, () => dataAdapter.getFeaturesArray(region, { statusCallback, stopToken }));
41
+ checkStopTokenThrottled(stopTokenCheck);
42
+ const { jexl } = pluginManager;
43
+ const shared = runTransforms(fetched, [
44
+ ...filters.map(expr => ({ type: 'filter', expr })),
45
+ ...transform,
46
+ ], jexl);
47
+ const layers = requested.map(({ encoding, lanes, transform: own }) => {
48
+ const features = own ? runTransforms(shared, own, jexl) : shared;
49
+ return encodeFeatures(features, encoding, lanes, {
50
+ jexl,
51
+ report: createProgressReporter({
52
+ label: 'Encoding features',
53
+ total: features.length,
54
+ statusCallback,
55
+ stopTokenCheck,
56
+ }),
57
+ });
58
+ });
59
+ const result = { layers, bytes };
60
+ return rpcResult(result, layers.flatMap(l => encodedChannelTransferables(l)));
61
+ }
62
+ }
@@ -5,7 +5,7 @@
5
5
  * Injective but NOT idempotent — escaping the marker is what buys injectivity,
6
6
  * so a second pass re-escapes the `~`s the first one wrote. Apply it exactly
7
7
  * once, at the place that emits both the id and the reference to it
8
- * (`SvgClipRect` and `SvgGradientLegend` already do; a hand-rolled `<clipPath>`
8
+ * (`SvgClipRect` and `SvgColorLegend` already do; a hand-rolled `<clipPath>`
9
9
  * must). Double-applying is harmless to correctness — the id and its `url()`
10
10
  * still agree, since both are derived from one call — but it produces noisy ids
11
11
  * for no reason.
package/esm/svg/svgId.js CHANGED
@@ -34,7 +34,7 @@ const UNSAFE_ID_CHAR = /[^\w.:-]/g;
34
34
  * Injective but NOT idempotent — escaping the marker is what buys injectivity,
35
35
  * so a second pass re-escapes the `~`s the first one wrote. Apply it exactly
36
36
  * once, at the place that emits both the id and the reference to it
37
- * (`SvgClipRect` and `SvgGradientLegend` already do; a hand-rolled `<clipPath>`
37
+ * (`SvgClipRect` and `SvgColorLegend` already do; a hand-rolled `<clipPath>`
38
38
  * must). Double-applying is harmless to correctness — the id and its `url()`
39
39
  * still agree, since both are derived from one call — but it produces noisy ids
40
40
  * for no reason.