@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
@@ -0,0 +1,439 @@
1
+ // The `cells` step: each row's runs against the reference it was fanned out
2
+ // of, and the texts its walk reads, which the fused identity kernel in
3
+ // cellMatches.ts walks too. Imported by relative path only.
4
+ import { DASH, SPACE, firstDrawn, isGapByte, isUnknownBase, lastDrawn, sameBase, } from './alignedBytes.js';
5
+ import { DerivedTable, numberReaderOf, readerOf, valueAt, } from './featureTable.js';
6
+ import { FannedTable, boundsThrough } from './stepTables.js';
7
+ export const DEFAULT_CELLS_FIELD = 'seq';
8
+ const CELL_STATES = ['match', 'mismatch', 'gap', 'insertion'];
9
+ export const MATCH = 0;
10
+ export const MISMATCH = 1;
11
+ const GAP = 2;
12
+ const INSERTION = 3;
13
+ export const NO_STATE = 255;
14
+ // A reference base's cell: no state outside the drawn columns, else the row's
15
+ // gap, or its base matched or not regardless of case.
16
+ export function cellState(refByte, rowByte, drawn) {
17
+ return !drawn
18
+ ? NO_STATE
19
+ : isGapByte(rowByte)
20
+ ? GAP
21
+ : sameBase(refByte, rowByte)
22
+ ? MATCH
23
+ : MISMATCH;
24
+ }
25
+ // Whether a cell counts toward identity: matched or not against a known
26
+ // reference base.
27
+ export function comparesBase(state, refByte) {
28
+ return state <= MISMATCH && !isUnknownBase(refByte);
29
+ }
30
+ // What the cells of one run share besides their state: whether they are
31
+ // compared, so a run's `match` is one value.
32
+ export function runKey(state, refByte) {
33
+ return state <= MISMATCH && isUnknownBase(refByte) ? 1 : 0;
34
+ }
35
+ // A mismatch always starts a run of its own, so each differing base is one
36
+ // instance and a text over `base` letters every one.
37
+ export function opensRun(state, key, runState, openKey) {
38
+ return state !== runState || key !== openKey || state === MISMATCH;
39
+ }
40
+ // The row a cell step reads against: past any table that only reordered or
41
+ // annotated the rows, the one they were fanned out of.
42
+ function containerOf(table) {
43
+ let at = table;
44
+ let toBase;
45
+ while (at instanceof DerivedTable && !at.madeFrom) {
46
+ toBase = at.parentRow ? compose(toBase, at.parentRow) : toBase;
47
+ at = at.parent;
48
+ }
49
+ if (!(at instanceof DerivedTable)) {
50
+ return undefined;
51
+ }
52
+ return {
53
+ made: at,
54
+ toMade: toBase,
55
+ table: at.parent,
56
+ row: at.parentRow ? compose(toBase, at.parentRow) : toBase,
57
+ };
58
+ }
59
+ function compose(outer, inner) {
60
+ if (!outer) {
61
+ return inner;
62
+ }
63
+ const out = new Uint32Array(outer.length);
64
+ for (let i = 0; i < outer.length; i++) {
65
+ out[i] = inner[outer[i]];
66
+ }
67
+ return out;
68
+ }
69
+ // Below this length a character loop copies a string faster than
70
+ // `encodeInto`, as the MAF packer measured.
71
+ const ASCII_COPY_MAX_LENGTH = 64;
72
+ const encoder = new TextEncoder();
73
+ // A column's text as bytes, one per character: an adapter's own, or the
74
+ // strings a feature holds copied into one buffer.
75
+ function textOf(column, n) {
76
+ if (column.kind === 'text') {
77
+ return column;
78
+ }
79
+ const read = readerOf(column);
80
+ const texts = new Array(n);
81
+ let total = 0;
82
+ for (let i = 0; i < n; i++) {
83
+ const v = read(i);
84
+ if (typeof v === 'string') {
85
+ texts[i] = v;
86
+ total += v.length;
87
+ }
88
+ }
89
+ const bytes = new Uint8Array(total);
90
+ const offset = new Uint32Array(n);
91
+ const length = new Uint32Array(n);
92
+ let pos = 0;
93
+ for (let i = 0; i < n; i++) {
94
+ const text = texts[i];
95
+ if (text === undefined) {
96
+ continue;
97
+ }
98
+ offset[i] = pos;
99
+ if (text.length <= ASCII_COPY_MAX_LENGTH) {
100
+ for (let c = 0; c < text.length; c++) {
101
+ bytes[pos + c] = text.charCodeAt(c);
102
+ }
103
+ length[i] = text.length;
104
+ }
105
+ else {
106
+ length[i] = encoder.encodeInto(text, bytes.subarray(pos, pos + text.length)).written;
107
+ }
108
+ pos += text.length;
109
+ }
110
+ return { kind: 'text', bytes, offset, length, at: undefined };
111
+ }
112
+ /**
113
+ * The texts a `cells` walk reads, by the row of the table it walked: each
114
+ * row's own, and the reference it is read against, which a row fanned out of
115
+ * a container reads off the container.
116
+ */
117
+ class WalkedTexts {
118
+ row;
119
+ reference;
120
+ refRow;
121
+ constructor(row, ref, refRow) {
122
+ this.row = row;
123
+ this.reference = ref;
124
+ this.refRow = refRow;
125
+ }
126
+ rowOf(r) {
127
+ const { at } = this.row;
128
+ return at ? at[r] : r;
129
+ }
130
+ refOf(r) {
131
+ const p = this.refRow ? this.refRow[r] : r;
132
+ const { at } = this.reference;
133
+ return at ? at[p] : p;
134
+ }
135
+ }
136
+ export function walkedTexts(table, field) {
137
+ const row = textOf(table.column(field), table.length);
138
+ const container = containerOf(table);
139
+ const made = container?.made;
140
+ if (container && !(made instanceof FannedTable && made.hasKept)) {
141
+ const { table: base } = container;
142
+ return new WalkedTexts(row, textOf(base.column(field), base.length), container.row);
143
+ }
144
+ // A container a flatten kept with nothing to fan out is the row itself, so
145
+ // its reference is its own container's, as its `parent()` says.
146
+ const byParent = (r) => table.row(r).parent?.()?.get(field);
147
+ const readContainer = container
148
+ ? readerOf(container.table.column(field))
149
+ : undefined;
150
+ const read = readContainer && made instanceof FannedTable
151
+ ? (r) => {
152
+ const { toMade, row: refRow } = container;
153
+ return made.isKept(toMade ? toMade[r] : r)
154
+ ? byParent(r)
155
+ : readContainer(refRow ? refRow[r] : r);
156
+ }
157
+ : byParent;
158
+ return new WalkedTexts(row, textOf({ kind: 'value', read }, table.length));
159
+ }
160
+ /**
161
+ * The runs a `cells` step answers, one per stretch of one state against the
162
+ * reference, typed: each run's span, its state and the text column it starts
163
+ * at, with `base`, `match` and `length` read back out of the texts when asked
164
+ * for.
165
+ */
166
+ class CellTable extends DerivedTable {
167
+ start;
168
+ end;
169
+ state;
170
+ textAt;
171
+ texts;
172
+ firstOfRow;
173
+ derived = new Map();
174
+ constructor(parent, parentRow, lanes, texts) {
175
+ super(parent, parentRow);
176
+ this.start = lanes.start;
177
+ this.end = lanes.end;
178
+ this.state = lanes.state;
179
+ this.textAt = lanes.textAt;
180
+ this.texts = texts;
181
+ }
182
+ get madeFrom() {
183
+ return true;
184
+ }
185
+ laneOf(name, build) {
186
+ let column = this.derived.get(name);
187
+ if (!column) {
188
+ column = build();
189
+ this.derived.set(name, column);
190
+ }
191
+ return column;
192
+ }
193
+ own(name) {
194
+ switch (name) {
195
+ case 'start': {
196
+ return { kind: 'number', values: this.start, at: undefined };
197
+ }
198
+ case 'end': {
199
+ return { kind: 'number', values: this.end, at: undefined };
200
+ }
201
+ case 'state': {
202
+ return {
203
+ kind: 'category',
204
+ codes: this.state,
205
+ labels: CELL_STATES,
206
+ at: undefined,
207
+ };
208
+ }
209
+ case 'match': {
210
+ return this.laneOf(name, () => {
211
+ const values = new Float32Array(this.length);
212
+ for (let i = 0; i < this.length; i++) {
213
+ const s = this.state[i];
214
+ values[i] = comparesBase(s, this.refByte(i))
215
+ ? s === MATCH
216
+ ? 1
217
+ : 0
218
+ : Number.NaN;
219
+ }
220
+ return { kind: 'number', values, at: undefined, nanIsAbsent: true };
221
+ });
222
+ }
223
+ case 'length': {
224
+ return this.laneOf(name, () => {
225
+ const values = new Float32Array(this.length).fill(Number.NaN);
226
+ for (let i = 0; i < this.length; i++) {
227
+ if (this.state[i] === INSERTION) {
228
+ values[i] = this.inserted(i).length;
229
+ }
230
+ }
231
+ return { kind: 'number', values, at: undefined, nanIsAbsent: true };
232
+ });
233
+ }
234
+ case 'base': {
235
+ return this.laneOf(name, () => {
236
+ const values = new Array(this.length);
237
+ for (let i = 0; i < this.length; i++) {
238
+ const s = this.state[i];
239
+ values[i] =
240
+ s === MISMATCH
241
+ ? this.mismatched(i)
242
+ : s === INSERTION
243
+ ? this.inserted(i)
244
+ : undefined;
245
+ }
246
+ return { kind: 'value', read: i => values[i] };
247
+ });
248
+ }
249
+ default: {
250
+ return undefined;
251
+ }
252
+ }
253
+ }
254
+ refByte(i) {
255
+ const { reference: ref } = this.texts;
256
+ const f = this.texts.refOf(this.parentOf(i));
257
+ return ref.bytes[ref.offset[f] + this.textAt[i]];
258
+ }
259
+ mismatched(i) {
260
+ const { bytes, offset } = this.texts.row;
261
+ const k = this.texts.rowOf(this.parentOf(i));
262
+ return String.fromCharCode(bytes[offset[k] + this.textAt[i]]);
263
+ }
264
+ // The bases a row holds where the reference has none, from the column the
265
+ // insertion starts at to the reference's next base.
266
+ inserted(i) {
267
+ const { row, reference: ref } = this.texts;
268
+ const r = this.parentOf(i);
269
+ const k = this.texts.rowOf(r);
270
+ const f = this.texts.refOf(r);
271
+ const rowAt = row.offset[k];
272
+ const refAt = ref.offset[f];
273
+ const end = Math.min(row.length[k], ref.length[f]);
274
+ let out = '';
275
+ for (let col = this.textAt[i]; col < end && ref.bytes[refAt + col] === DASH; col++) {
276
+ const b = row.bytes[rowAt + col];
277
+ if (!isGapByte(b)) {
278
+ out += String.fromCharCode(b);
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+ id(i) {
284
+ const p = this.parentOf(i);
285
+ this.firstOfRow ??= firstRowIndex(this.parentRow, this.parent.length);
286
+ return `${this.parent.row(p).id()}#${i - this.firstOfRow[p]}`;
287
+ }
288
+ json(i) {
289
+ const out = {
290
+ ...this.parent.row(this.parentOf(i)).toJSON(),
291
+ start: this.start[i],
292
+ end: this.end[i],
293
+ state: CELL_STATES[this.state[i]],
294
+ };
295
+ for (const name of ['base', 'match', 'length']) {
296
+ const v = valueAt(this.column(name), i);
297
+ if (v !== undefined) {
298
+ out[name] = v;
299
+ }
300
+ }
301
+ return out;
302
+ }
303
+ }
304
+ // The lanes a `cells` walk writes, doubled as a row would overrun them.
305
+ class RunLanes {
306
+ start;
307
+ end;
308
+ state;
309
+ textAt;
310
+ parentRow;
311
+ length = 0;
312
+ constructor(capacity) {
313
+ this.start = new Uint32Array(capacity);
314
+ this.end = new Uint32Array(capacity);
315
+ this.state = new Uint8Array(capacity);
316
+ this.textAt = new Uint32Array(capacity);
317
+ this.parentRow = new Uint32Array(capacity);
318
+ }
319
+ reserve(more) {
320
+ const need = this.length + more;
321
+ if (need <= this.start.length) {
322
+ return;
323
+ }
324
+ const size = Math.max(this.start.length * 2, need);
325
+ const widen = (a) => {
326
+ const next = new a.constructor(size);
327
+ next.set(a);
328
+ return next;
329
+ };
330
+ this.start = widen(this.start);
331
+ this.end = widen(this.end);
332
+ this.state = widen(this.state);
333
+ this.textAt = widen(this.textAt);
334
+ this.parentRow = widen(this.parentRow);
335
+ }
336
+ // One row's runs against its reference: a run per column is the most it
337
+ // can answer, so the lanes grow once and the walk writes through locals.
338
+ walk(r, startPos, rowBytes, rowAt, rowLen, refBytes, refAt, refLen) {
339
+ this.reserve(refLen + 1);
340
+ const { start, end, state, textAt, parentRow } = this;
341
+ let n = this.length;
342
+ let pos = startPos;
343
+ const first = firstDrawn(rowBytes, rowAt, rowLen);
344
+ const last = lastDrawn(rowBytes, rowAt, rowLen, first);
345
+ let runStart = -1;
346
+ let runState = NO_STATE;
347
+ let openKey = -1;
348
+ let runCol = 0;
349
+ let insertAt = -1;
350
+ for (let col = 0; col < refLen; col++) {
351
+ const refByte = refBytes[refAt + col];
352
+ const rowByte = col < rowLen ? rowBytes[rowAt + col] : SPACE;
353
+ if (refByte === DASH) {
354
+ if (insertAt < 0 && col < rowLen && !isGapByte(rowByte)) {
355
+ insertAt = col;
356
+ }
357
+ continue;
358
+ }
359
+ if (insertAt >= 0) {
360
+ start[n] = pos;
361
+ end[n] = pos;
362
+ state[n] = INSERTION;
363
+ textAt[n] = insertAt;
364
+ parentRow[n] = r;
365
+ n++;
366
+ insertAt = -1;
367
+ }
368
+ const st = cellState(refByte, rowByte, col >= first && col <= last && col < rowLen);
369
+ const key = runKey(st, refByte);
370
+ if (opensRun(st, key, runState, openKey)) {
371
+ if (runStart >= 0) {
372
+ start[n] = runStart;
373
+ end[n] = pos;
374
+ state[n] = runState;
375
+ textAt[n] = runCol;
376
+ parentRow[n] = r;
377
+ n++;
378
+ }
379
+ runStart = st === NO_STATE ? -1 : pos;
380
+ runState = st;
381
+ openKey = key;
382
+ runCol = col;
383
+ }
384
+ pos++;
385
+ }
386
+ if (runStart >= 0) {
387
+ start[n] = runStart;
388
+ end[n] = pos;
389
+ state[n] = runState;
390
+ textAt[n] = runCol;
391
+ parentRow[n] = r;
392
+ n++;
393
+ }
394
+ this.length = n;
395
+ }
396
+ }
397
+ // Where each parent row's children start, for children written parent by
398
+ // parent in order.
399
+ function firstRowIndex(parentRow, parents) {
400
+ const first = new Uint32Array(parents);
401
+ for (let i = parentRow.length - 1; i >= 0; i--) {
402
+ first[parentRow[i]] = i;
403
+ }
404
+ return first;
405
+ }
406
+ export function cells({ table, bounds }, step) {
407
+ const { field = DEFAULT_CELLS_FIELD } = step;
408
+ const texts = walkedTexts(table, field);
409
+ const readStart = numberReaderOf(table.column('start'));
410
+ const { bytes: rowBytes, offset: rowOffset, length: rowLength } = texts.row;
411
+ const { bytes: refBytes, offset: refOffset, length: refLength, } = texts.reference;
412
+ // A run per reference base is the most a row can answer and far past what
413
+ // one does, so the lanes start at an eighth of it.
414
+ let bound = 0;
415
+ for (let r = 0; r < table.length; r++) {
416
+ bound += refLength[texts.refOf(r)] + 1;
417
+ }
418
+ const lanes = new RunLanes(Math.max(1024, bound >>> 3));
419
+ for (let r = 0; r < table.length; r++) {
420
+ const f = texts.refOf(r);
421
+ const refLen = refLength[f];
422
+ const pos = readStart(r);
423
+ if (refLen > 0 && pos >= 0) {
424
+ const k = texts.rowOf(r);
425
+ lanes.walk(r, pos, rowBytes, rowOffset[k], rowLength[k], refBytes, refOffset[f], refLen);
426
+ }
427
+ }
428
+ const n = lanes.length;
429
+ const rows = lanes.parentRow.subarray(0, n);
430
+ return {
431
+ table: new CellTable(table, rows, {
432
+ start: lanes.start.subarray(0, n),
433
+ end: lanes.end.subarray(0, n),
434
+ state: lanes.state.subarray(0, n),
435
+ textAt: lanes.textAt.subarray(0, n),
436
+ }, texts),
437
+ bounds: boundsThrough(bounds, rows),
438
+ };
439
+ }
@@ -239,16 +239,27 @@ export function parseColor(color) {
239
239
  }
240
240
  throw new Error(`Color.parse(): invalid CSS color: "${color}"`);
241
241
  }
242
+ /** A channel's leading number, throwing where it has none, as `rgb(a,b,1)`. */
243
+ function parseNumber(value) {
244
+ const n = Number.parseFloat(value);
245
+ if (Number.isNaN(n)) {
246
+ throw new TypeError(`Color.parse(): invalid CSS color channel: "${value}"`);
247
+ }
248
+ return n;
249
+ }
242
250
  /**
243
- * Accepts: "50%", "128"
251
+ * Accepts: "50%", "128", "none"
244
252
  * https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb#values
245
253
  * @returns a value in the 0 to 255 range
246
254
  */
247
255
  function parseColorChannel(channel) {
256
+ if (channel === 'none') {
257
+ return 0;
258
+ }
248
259
  if (channel.charCodeAt(channel.length - 1) === PERCENT) {
249
- return Math.round((Number.parseFloat(channel) / 100) * 255);
260
+ return Math.round((parseNumber(channel) / 100) * 255);
250
261
  }
251
- return Math.round(Number.parseFloat(channel));
262
+ return Math.round(parseNumber(channel));
252
263
  }
253
264
  /**
254
265
  * Accepts: "50%", ".5", "0.5"
@@ -268,9 +279,9 @@ function parseAlphaValue(channel) {
268
279
  return 0;
269
280
  }
270
281
  if (channel.charCodeAt(channel.length - 1) === PERCENT) {
271
- return Number.parseFloat(channel) / 100;
282
+ return parseNumber(channel) / 100;
272
283
  }
273
- return Number.parseFloat(channel);
284
+ return parseNumber(channel);
274
285
  }
275
286
  /**
276
287
  * Accepts: "360", "360deg", "400grad", "6.28rad", "1turn", "none"
@@ -302,7 +313,7 @@ function parseAngle(angle) {
302
313
  factor = 360;
303
314
  }
304
315
  }
305
- const turns = Number.parseFloat(angle) / factor;
316
+ const turns = parseNumber(angle) / factor;
306
317
  // Hue is periodic, and every caller of this is a hue. `hueToRGB` corrects its
307
318
  // argument by at most one turn, so it reads `hsl(-720deg)` as a hue two turns
308
319
  // below the wheel rather than as red — folding here is what makes the three
@@ -317,7 +328,7 @@ function parsePercentage(value) {
317
328
  if (value.charCodeAt(0) === N) {
318
329
  return 0;
319
330
  }
320
- return Number.parseFloat(value) / 100;
331
+ return parseNumber(value) / 100;
321
332
  }
322
333
  /**
323
334
  * Accepts: "1.0", "100%", "none"
@@ -328,9 +339,9 @@ function parsePercentageOrValue(value) {
328
339
  return 0;
329
340
  }
330
341
  if (value.charCodeAt(value.length - 1) === PERCENT) {
331
- return Number.parseFloat(value) / 100;
342
+ return parseNumber(value) / 100;
332
343
  }
333
- return Number.parseFloat(value);
344
+ return parseNumber(value);
334
345
  }
335
346
  /**
336
347
  * Accepts: "100", "100%", "none"
@@ -341,9 +352,9 @@ function parsePercentageFor(value, range) {
341
352
  return 0;
342
353
  }
343
354
  if (value.charCodeAt(value.length - 1) === PERCENT) {
344
- return (Number.parseFloat(value) / 100) * range;
355
+ return (parseNumber(value) / 100) * range;
345
356
  }
346
- return Number.parseFloat(value);
357
+ return parseNumber(value);
347
358
  }
348
359
  // HSL functions
349
360
  function hueToRGB(p, q, t) {
@@ -90,6 +90,11 @@ const PURPLEORANGE_STOPS = [
90
90
  [179, 88, 6, 255],
91
91
  [127, 59, 8, 255],
92
92
  ];
93
+ const REDGREYBLUE_STOPS = [
94
+ [199, 67, 56, 255],
95
+ [140, 140, 140, 255],
96
+ [47, 102, 176, 255],
97
+ ];
93
98
  const SCHEME_STOPS = {
94
99
  viridis: VIRIDIS_STOPS,
95
100
  juicebox: JUICEBOX_STOPS,
@@ -101,6 +106,7 @@ const SCHEME_STOPS = {
101
106
  cividis: stopsFromHex(CIVIDIS_HEX_SPEC),
102
107
  redblue: REDBLUE_STOPS,
103
108
  purpleorange: PURPLEORANGE_STOPS,
109
+ redgreyblue: REDGREYBLUE_STOPS,
104
110
  };
105
111
  function luminanceOverWhite([r, g, b, a]) {
106
112
  const over = (c) => 255 + (c - 255) * (a / 255);
@@ -36,8 +36,8 @@ export interface FieldPreset<S extends string = ColorScaleName> {
36
36
  export type FieldPresets<S extends string = ColorScaleName> = Readonly<Record<string, FieldPreset<S>>>;
37
37
  /**
38
38
  * The fields whose values have names, an order and colours of their own on
39
- * every display, beneath each display's own presets. Red forward and blue
40
- * reverse is what the synteny ribbons paint too. The SV classes are
39
+ * every display, beneath each display's own presets. The synteny ribbons
40
+ * paint strand from this range too. The SV classes are
41
41
  * `svClassOf`'s, deletion red and duplication blue as dbVar and gnomAD-SV
42
42
  * paint them, insertion the pileup's purple, and the rest kept apart under
43
43
  * deuteranopia and protanopia. A record with no class files under `''`, so
@@ -143,6 +143,13 @@ export interface ColorSlots {
143
143
  domainQuantile?: number;
144
144
  labels?: readonly unknown[];
145
145
  }
146
+ /** A quantitative scale's ends among the members `member` reads, each a number or unset. */
147
+ export declare function scaleEndsOf(member: (name: string) => unknown): ScaleEnds;
148
+ /**
149
+ * The members `colorProblems` reads among the ones `member` reads, each of the
150
+ * type it reads or unset, so a config snapshot and a config node answer alike.
151
+ */
152
+ export declare function colorSlotsOf(member: (name: string) => unknown): ColorSlots;
146
153
  /**
147
154
  * What a colour object's slots say together that no single slot can refuse,
148
155
  * since the config editor writes one slot at a time (ADR-133), read with its
@@ -20,8 +20,8 @@ export const IDENTITY_SCALE = 'identity';
20
20
  const CATEGORICAL_PRESET = { scale: 'categorical' };
21
21
  /**
22
22
  * The fields whose values have names, an order and colours of their own on
23
- * every display, beneath each display's own presets. Red forward and blue
24
- * reverse is what the synteny ribbons paint too. The SV classes are
23
+ * every display, beneath each display's own presets. The synteny ribbons
24
+ * paint strand from this range too. The SV classes are
25
25
  * `svClassOf`'s, deletion red and duplication blue as dbVar and gnomAD-SV
26
26
  * paint them, insertion the pileup's purple, and the rest kept apart under
27
27
  * deuteranopia and protanopia. A record with no class files under `''`, so
@@ -165,6 +165,37 @@ export function scaleEndProblems({ domainMin, domainMax, domainQuantile }, { rev
165
165
  export function noticeLines(setting, problems) {
166
166
  return problems.map(({ slot, message }) => `${setting}.${slot}: ${message}`);
167
167
  }
168
+ function numberOf(value) {
169
+ return typeof value === 'number' ? value : undefined;
170
+ }
171
+ function stringOf(value) {
172
+ return typeof value === 'string' ? value : undefined;
173
+ }
174
+ function listOf(value) {
175
+ return Array.isArray(value) ? value : undefined;
176
+ }
177
+ /** A quantitative scale's ends among the members `member` reads, each a number or unset. */
178
+ export function scaleEndsOf(member) {
179
+ return {
180
+ domainMin: numberOf(member('domainMin')),
181
+ domainMax: numberOf(member('domainMax')),
182
+ domainQuantile: numberOf(member('domainQuantile')),
183
+ };
184
+ }
185
+ /**
186
+ * The members `colorProblems` reads among the ones `member` reads, each of the
187
+ * type it reads or unset, so a config snapshot and a config node answer alike.
188
+ */
189
+ export function colorSlotsOf(member) {
190
+ return {
191
+ ...scaleEndsOf(member),
192
+ field: stringOf(member('field')) ?? '',
193
+ scale: stringOf(member('scale')),
194
+ domain: listOf(member('domain')),
195
+ range: listOf(member('range')),
196
+ labels: listOf(member('labels')),
197
+ };
198
+ }
168
199
  function pinned(entry) {
169
200
  return entry !== '' && Number.isFinite(Number(entry));
170
201
  }
@@ -7,9 +7,11 @@
7
7
  * transparent to red, as Juicebox paints contacts; `fall` runs white through
8
8
  * yellow and red to black, as HiGlass does; `reds` and `blues` are
9
9
  * ColorBrewer's, from white; `redblue` and `purpleorange` diverge through
10
- * white, ColorBrewer's RdBu and PuOr.
10
+ * white, ColorBrewer's RdBu and PuOr; `redgreyblue` diverges through a grey
11
+ * middle, which stays visible on a white page where a white middle would not,
12
+ * as the MAF display's identity ramp paints it.
11
13
  */
12
- export declare const COLOR_SCHEMES: readonly ['viridis', 'magma', 'inferno', 'cividis', 'juicebox', 'fall', 'reds', 'blues', 'redblue', 'purpleorange'];
14
+ export declare const COLOR_SCHEMES: readonly ['viridis', 'magma', 'inferno', 'cividis', 'juicebox', 'fall', 'reds', 'blues', 'redblue', 'purpleorange', 'redgreyblue'];
13
15
  /** #api */
14
16
  export type ColorSchemeName = (typeof COLOR_SCHEMES)[number];
15
17
  /**
@@ -7,7 +7,9 @@
7
7
  * transparent to red, as Juicebox paints contacts; `fall` runs white through
8
8
  * yellow and red to black, as HiGlass does; `reds` and `blues` are
9
9
  * ColorBrewer's, from white; `redblue` and `purpleorange` diverge through
10
- * white, ColorBrewer's RdBu and PuOr.
10
+ * white, ColorBrewer's RdBu and PuOr; `redgreyblue` diverges through a grey
11
+ * middle, which stays visible on a white page where a white middle would not,
12
+ * as the MAF display's identity ramp paints it.
11
13
  */
12
14
  export const COLOR_SCHEMES = [
13
15
  'viridis',
@@ -20,6 +22,7 @@ export const COLOR_SCHEMES = [
20
22
  'blues',
21
23
  'redblue',
22
24
  'purpleorange',
25
+ 'redgreyblue',
23
26
  ];
24
27
  /**
25
28
  * #api
@@ -7,7 +7,14 @@ export default async function copyToClipboard(text, options = {}) {
7
7
  // navigator.clipboard is only present in secure contexts; isSecureContext is
8
8
  // the real gate, so insecure http:// falls through to execCommand below
9
9
  if (format === 'text/plain' && window.isSecureContext) {
10
- await navigator.clipboard.writeText(text);
10
+ try {
11
+ await navigator.clipboard.writeText(text);
12
+ }
13
+ catch (e) {
14
+ if (!execCommandCopy(text, format)) {
15
+ throw e;
16
+ }
17
+ }
11
18
  }
12
19
  else if (!execCommandCopy(text, format)) {
13
20
  throw new Error('the browser rejected the clipboard write');
@@ -81,9 +81,9 @@ export function createAbortRotation(self, report) {
81
81
  // Opened before the superseded slot retires, so the window is never idle
82
82
  // across the handover and the label on screen survives it (ADR-080).
83
83
  // Retired here rather than left to the superseded run's `finally`, which
84
- // comes only when its work settles — and a CRAM record read or a shared
85
- // parse the signal is withheld from runs on past the abort, its last
86
- // reading summed into this fetch's bar the whole time.
84
+ // comes only when its work settles — and a CRAM record read, or a shared
85
+ // `cachedSetup` parse the replacement rejoins, runs on past the abort,
86
+ // its last reading summed into this fetch's bar the whole time.
87
87
  const stream = statusWindow.open({ isCurrent });
88
88
  openStream?.clear();
89
89
  openStream = stream;