@genome-spy/core 0.71.0 → 0.73.0

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 (121) hide show
  1. package/LICENSE +1 -1
  2. package/dist/bundle/index.es.js +6842 -5365
  3. package/dist/bundle/index.js +159 -140
  4. package/dist/bundle/parquetRead-BnAGCa4_.js +1663 -0
  5. package/dist/schema.json +281 -17
  6. package/dist/src/data/formats/bed.d.ts +8 -0
  7. package/dist/src/data/formats/bed.d.ts.map +1 -0
  8. package/dist/src/data/formats/bed.js +53 -0
  9. package/dist/src/data/formats/bedpe.d.ts +8 -0
  10. package/dist/src/data/formats/bedpe.d.ts.map +1 -0
  11. package/dist/src/data/formats/bedpe.js +160 -0
  12. package/dist/src/data/formats/parquet.d.ts +12 -0
  13. package/dist/src/data/formats/parquet.d.ts.map +1 -0
  14. package/dist/src/data/formats/parquet.js +29 -0
  15. package/dist/src/data/formats/parquetRead.d.ts +18 -0
  16. package/dist/src/data/formats/parquetRead.d.ts.map +1 -0
  17. package/dist/src/data/formats/parquetRead.js +326 -0
  18. package/dist/src/data/sources/dataUtils.d.ts +16 -0
  19. package/dist/src/data/sources/dataUtils.d.ts.map +1 -1
  20. package/dist/src/data/sources/dataUtils.js +53 -3
  21. package/dist/src/data/sources/urlSource.d.ts +4 -0
  22. package/dist/src/data/sources/urlSource.d.ts.map +1 -1
  23. package/dist/src/data/sources/urlSource.js +141 -17
  24. package/dist/src/encoder/encoder.d.ts +2 -2
  25. package/dist/src/fonts/bmFontManager.d.ts +1 -1
  26. package/dist/src/genome/assemblyPreflight.d.ts +31 -0
  27. package/dist/src/genome/assemblyPreflight.d.ts.map +1 -0
  28. package/dist/src/genome/assemblyPreflight.js +99 -0
  29. package/dist/src/genome/genome.d.ts +2 -2
  30. package/dist/src/genome/genome.d.ts.map +1 -1
  31. package/dist/src/genome/genome.js +4 -0
  32. package/dist/src/genome/genomeStore.d.ts +34 -3
  33. package/dist/src/genome/genomeStore.d.ts.map +1 -1
  34. package/dist/src/genome/genomeStore.js +409 -18
  35. package/dist/src/genome/rootGenomeConfig.d.ts +26 -0
  36. package/dist/src/genome/rootGenomeConfig.d.ts.map +1 -0
  37. package/dist/src/genome/rootGenomeConfig.js +94 -0
  38. package/dist/src/genomeSpy/interactionController.d.ts +5 -1
  39. package/dist/src/genomeSpy/interactionController.d.ts.map +1 -1
  40. package/dist/src/genomeSpy/interactionController.js +244 -29
  41. package/dist/src/genomeSpy/renderCoordinator.js +1 -1
  42. package/dist/src/genomeSpy.d.ts +13 -3
  43. package/dist/src/genomeSpy.d.ts.map +1 -1
  44. package/dist/src/genomeSpy.js +83 -7
  45. package/dist/src/gl/canvasSizeHelper.d.ts +74 -0
  46. package/dist/src/gl/canvasSizeHelper.d.ts.map +1 -0
  47. package/dist/src/gl/canvasSizeHelper.js +203 -0
  48. package/dist/src/gl/hashTable.d.ts +78 -0
  49. package/dist/src/gl/hashTable.d.ts.map +1 -0
  50. package/dist/src/gl/hashTable.js +164 -0
  51. package/dist/src/gl/includes/common.glsl.js +1 -1
  52. package/dist/src/gl/webGLHelper.d.ts +25 -11
  53. package/dist/src/gl/webGLHelper.d.ts.map +1 -1
  54. package/dist/src/gl/webGLHelper.js +71 -39
  55. package/dist/src/index.d.ts.map +1 -1
  56. package/dist/src/index.js +5 -2
  57. package/dist/src/marks/link.d.ts.map +1 -1
  58. package/dist/src/marks/link.js +5 -3
  59. package/dist/src/marks/mark.d.ts +1 -1
  60. package/dist/src/marks/mark.d.ts.map +1 -1
  61. package/dist/src/marks/mark.js +8 -4
  62. package/dist/src/scales/domainPlanner.d.ts +34 -3
  63. package/dist/src/scales/domainPlanner.d.ts.map +1 -1
  64. package/dist/src/scales/domainPlanner.js +247 -26
  65. package/dist/src/scales/scaleInstanceManager.d.ts +2 -1
  66. package/dist/src/scales/scaleInstanceManager.d.ts.map +1 -1
  67. package/dist/src/scales/scaleInstanceManager.js +10 -11
  68. package/dist/src/scales/scaleInteractionController.d.ts.map +1 -1
  69. package/dist/src/scales/scaleInteractionController.js +16 -14
  70. package/dist/src/scales/scaleResolution.d.ts +16 -0
  71. package/dist/src/scales/scaleResolution.d.ts.map +1 -1
  72. package/dist/src/scales/scaleResolution.js +314 -54
  73. package/dist/src/scales/scaleResolutionTestUtils.d.ts +21 -0
  74. package/dist/src/scales/scaleResolutionTestUtils.d.ts.map +1 -0
  75. package/dist/src/scales/scaleResolutionTestUtils.js +33 -0
  76. package/dist/src/scales/selectionDomainUtils.d.ts +22 -0
  77. package/dist/src/scales/selectionDomainUtils.d.ts.map +1 -0
  78. package/dist/src/scales/selectionDomainUtils.js +79 -0
  79. package/dist/src/scales/zoomDomainUtils.d.ts +18 -0
  80. package/dist/src/scales/zoomDomainUtils.d.ts.map +1 -0
  81. package/dist/src/scales/zoomDomainUtils.js +69 -0
  82. package/dist/src/screenshotHarness.d.ts +16 -0
  83. package/dist/src/screenshotHarness.d.ts.map +1 -0
  84. package/dist/src/screenshotHarness.js +242 -0
  85. package/dist/src/singlePageApp.js +1 -1
  86. package/dist/src/spec/data.d.ts +23 -3
  87. package/dist/src/spec/genome.d.ts +22 -2
  88. package/dist/src/spec/parameter.d.ts +39 -2
  89. package/dist/src/spec/root.d.ts +20 -1
  90. package/dist/src/spec/scale.d.ts +41 -5
  91. package/dist/src/styles/genome-spy.css +8 -0
  92. package/dist/src/styles/genome-spy.css.d.ts +1 -1
  93. package/dist/src/styles/genome-spy.css.d.ts.map +1 -1
  94. package/dist/src/styles/genome-spy.css.js +8 -0
  95. package/dist/src/tooltip/dataTooltipHandler.js +59 -10
  96. package/dist/src/types/embedApi.d.ts +19 -0
  97. package/dist/src/utils/inferSpecBaseUrl.d.ts +14 -0
  98. package/dist/src/utils/inferSpecBaseUrl.d.ts.map +1 -0
  99. package/dist/src/utils/inferSpecBaseUrl.js +73 -0
  100. package/dist/src/utils/interactionEvent.d.ts +53 -3
  101. package/dist/src/utils/interactionEvent.d.ts.map +1 -1
  102. package/dist/src/utils/interactionEvent.js +62 -1
  103. package/dist/src/utils/radixSort.d.ts.map +1 -1
  104. package/dist/src/utils/radixSort.js +26 -1
  105. package/dist/src/view/containerMutationHelper.d.ts.map +1 -1
  106. package/dist/src/view/containerMutationHelper.js +8 -0
  107. package/dist/src/view/dataReadiness.d.ts +2 -2
  108. package/dist/src/view/dataReadiness.d.ts.map +1 -1
  109. package/dist/src/view/dataReadiness.js +63 -58
  110. package/dist/src/view/facetView.d.ts +1 -1
  111. package/dist/src/view/facetView.js +1 -1
  112. package/dist/src/view/gridView/gridChild.d.ts +7 -0
  113. package/dist/src/view/gridView/gridChild.d.ts.map +1 -1
  114. package/dist/src/view/gridView/gridChild.js +180 -11
  115. package/dist/src/view/gridView/gridView.d.ts.map +1 -1
  116. package/dist/src/view/gridView/gridView.js +60 -17
  117. package/dist/src/view/unitView.d.ts +1 -1
  118. package/dist/src/view/zoom.d.ts +14 -2
  119. package/dist/src/view/zoom.d.ts.map +1 -1
  120. package/dist/src/view/zoom.js +373 -76
  121. package/package.json +5 -2
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @typedef {import("../types/encoder.js").VegaScale} VegaScale
3
+ */
4
+ /**
5
+ * Zooms a numeric two-point domain according to scale type.
6
+ *
7
+ * @param {VegaScale} scale
8
+ * @param {[number, number]} domain
9
+ * @param {number} anchor
10
+ * @param {number} scaleFactor
11
+ * @param {{ onUnsupported?: "throw" | "identity" }} [options]
12
+ * @returns {[number, number]}
13
+ */
14
+ export function zoomDomainByScaleType(scale: VegaScale, domain: [number, number], anchor: number, scaleFactor: number, options?: {
15
+ onUnsupported?: "throw" | "identity";
16
+ }): [number, number];
17
+ export type VegaScale = import("../types/encoder.js").VegaScale;
18
+ //# sourceMappingURL=zoomDomainUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zoomDomainUtils.d.ts","sourceRoot":"","sources":["../../../src/scales/zoomDomainUtils.js"],"names":[],"mappings":"AAEA;;GAEG;AAEH;;;;;;;;;GASG;AACH,6CAPW,SAAS,UACT,CAAC,MAAM,EAAE,MAAM,CAAC,UAChB,MAAM,eACN,MAAM,YACN;IAAE,aAAa,CAAC,EAAE,OAAO,GAAG,UAAU,CAAA;CAAE,GACtC,CAAC,MAAM,EAAE,MAAM,CAAC,CAsD5B;wBAjEY,OAAO,qBAAqB,EAAE,SAAS"}
@@ -0,0 +1,69 @@
1
+ import { zoomLinear, zoomLog, zoomPow, zoomSymlog } from "vega-util";
2
+
3
+ /**
4
+ * @typedef {import("../types/encoder.js").VegaScale} VegaScale
5
+ */
6
+
7
+ /**
8
+ * Zooms a numeric two-point domain according to scale type.
9
+ *
10
+ * @param {VegaScale} scale
11
+ * @param {[number, number]} domain
12
+ * @param {number} anchor
13
+ * @param {number} scaleFactor
14
+ * @param {{ onUnsupported?: "throw" | "identity" }} [options]
15
+ * @returns {[number, number]}
16
+ */
17
+ export function zoomDomainByScaleType(
18
+ scale,
19
+ domain,
20
+ anchor,
21
+ scaleFactor,
22
+ options = {}
23
+ ) {
24
+ const onUnsupported = options.onUnsupported ?? "throw";
25
+
26
+ switch (scale.type) {
27
+ case "linear":
28
+ case "index":
29
+ case "locus":
30
+ return /** @type {[number, number]} */ (
31
+ zoomLinear(domain, anchor, scaleFactor)
32
+ );
33
+
34
+ case "log":
35
+ return /** @type {[number, number]} */ (
36
+ zoomLog(domain, anchor, scaleFactor)
37
+ );
38
+
39
+ case "pow":
40
+ case "sqrt": {
41
+ const powScale =
42
+ /** @type {import("d3-scale").ScalePower<number, number>} */ (
43
+ /** @type {any} */ (scale)
44
+ );
45
+ return /** @type {[number, number]} */ (
46
+ zoomPow(domain, anchor, scaleFactor, powScale.exponent())
47
+ );
48
+ }
49
+
50
+ case "symlog": {
51
+ const symlogScale =
52
+ /** @type {import("d3-scale").ScaleSymLog<number, number>} */ (
53
+ /** @type {any} */ (scale)
54
+ );
55
+ return /** @type {[number, number]} */ (
56
+ zoomSymlog(domain, anchor, scaleFactor, symlogScale.constant())
57
+ );
58
+ }
59
+
60
+ default:
61
+ if (onUnsupported === "identity") {
62
+ return domain;
63
+ } else {
64
+ throw new Error(
65
+ "Zooming is not implemented for: " + scale.type
66
+ );
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,16 @@
1
+ export type ScreenshotState = {
2
+ status: string;
3
+ detail: string;
4
+ error: string;
5
+ capture: () => Promise<{
6
+ logicalSize: {
7
+ width: number;
8
+ height: number;
9
+ };
10
+ dataUrl: string;
11
+ }>;
12
+ };
13
+ export type ScreenshotWindow = Window & typeof globalThis & {
14
+ __genomeSpyScreenshot: ScreenshotState;
15
+ };
16
+ //# sourceMappingURL=screenshotHarness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshotHarness.d.ts","sourceRoot":"","sources":["../../src/screenshotHarness.js"],"names":[],"mappings":"8BAQa;IACR,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,OAAO,CAAC;QAAE,WAAW,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC5F;+BAIS,MAAM,GAAG,OAAO,UAAU,GAAG;IAAE,qBAAqB,EAAE,eAAe,CAAA;CAAE"}
@@ -0,0 +1,242 @@
1
+ import { embed, loadSpec } from "./index.js";
2
+
3
+ const DEFAULT_CONTAINER_WIDTH = 600;
4
+ const DEFAULT_CONTAINER_HEIGHT = 320;
5
+ const DEFAULT_LAZY_READY_TIMEOUT_MS = 30_000;
6
+ const READY_DELAY_MS = 100;
7
+
8
+ /**
9
+ * @typedef {{
10
+ * status: string,
11
+ * detail: string,
12
+ * error: string,
13
+ * capture: () => Promise<{ logicalSize: { width: number, height: number }, dataUrl: string }>
14
+ * }} ScreenshotState
15
+ */
16
+
17
+ /**
18
+ * @typedef {Window & typeof globalThis & { __genomeSpyScreenshot: ScreenshotState }} ScreenshotWindow
19
+ */
20
+
21
+ const frameElement = /** @type {HTMLElement} */ (
22
+ document.querySelector("#frame")
23
+ );
24
+ const specPathElement = /** @type {HTMLElement} */ (
25
+ document.querySelector("#spec-path")
26
+ );
27
+ const statusElement = /** @type {HTMLElement} */ (
28
+ document.querySelector("#status")
29
+ );
30
+ const screenshotWindow = /** @type {ScreenshotWindow} */ (window);
31
+
32
+ const query = new URLSearchParams(window.location.search);
33
+ const specUrl = query.get("spec");
34
+ const lazyReadyTimeoutMs = parseTimeoutMs(
35
+ query.get("lazy-timeout-ms"),
36
+ DEFAULT_LAZY_READY_TIMEOUT_MS
37
+ );
38
+
39
+ screenshotWindow.__genomeSpyScreenshot = {
40
+ status: "booting",
41
+ detail: "Booting screenshot harness",
42
+ error: "",
43
+ async capture() {
44
+ throw new Error("Screenshot harness is not ready yet.");
45
+ },
46
+ };
47
+
48
+ if (!specUrl) {
49
+ setFailure("Missing required ?spec=... query parameter.");
50
+ } else {
51
+ specPathElement.textContent = specUrl;
52
+ frameElement.style.width = `${DEFAULT_CONTAINER_WIDTH}px`;
53
+ frameElement.style.height = `${DEFAULT_CONTAINER_HEIGHT}px`;
54
+ void initializeHarness(specUrl);
55
+ }
56
+
57
+ /**
58
+ * @param {string} url
59
+ */
60
+ async function initializeHarness(url) {
61
+ try {
62
+ setState("loading", "Loading spec…");
63
+ await loadSpec(url);
64
+
65
+ setState("embedding", "Embedding visualization…");
66
+ const api = await embed(frameElement, url, {
67
+ onError(error) {
68
+ throw error;
69
+ },
70
+ });
71
+
72
+ if (
73
+ typeof api.getLogicalCanvasSize !== "function" ||
74
+ typeof api.getRenderedBounds !== "function" ||
75
+ typeof api.awaitVisibleLazyData !== "function"
76
+ ) {
77
+ throw new Error(
78
+ "Embed did not return a usable GenomeSpy instance."
79
+ );
80
+ }
81
+
82
+ setState("waitingForData", "Waiting for visible lazy data…");
83
+ await waitForVisibleLazyData(api, lazyReadyTimeoutMs);
84
+
85
+ setState("rendering", "Waiting for initial render…");
86
+ await waitForSettledRender();
87
+
88
+ const logicalSize = resolveExportSize(
89
+ api.getRenderedBounds(),
90
+ api.getLogicalCanvasSize()
91
+ );
92
+
93
+ screenshotWindow.__genomeSpyScreenshot = {
94
+ status: "ready",
95
+ detail: `Ready (${logicalSize.width}x${logicalSize.height}, DPR 1)`,
96
+ error: "",
97
+ async capture() {
98
+ const currentSize = resolveExportSize(
99
+ api.getRenderedBounds(),
100
+ api.getLogicalCanvasSize()
101
+ );
102
+ return {
103
+ logicalSize: currentSize,
104
+ dataUrl: api.exportCanvas(
105
+ currentSize.width,
106
+ currentSize.height,
107
+ 1,
108
+ "white"
109
+ ),
110
+ };
111
+ },
112
+ };
113
+
114
+ setStatus(
115
+ `Ready (${logicalSize.width}x${logicalSize.height}, DPR 1)`
116
+ );
117
+ } catch (error) {
118
+ setFailure(error instanceof Error ? error.message : String(error));
119
+ }
120
+ }
121
+
122
+ async function waitForSettledRender() {
123
+ await waitForAnimationFrame();
124
+ await waitForAnimationFrame();
125
+ await wait(READY_DELAY_MS);
126
+ }
127
+
128
+ /**
129
+ * @param {{
130
+ * awaitVisibleLazyData: (signal?: AbortSignal) => Promise<void>
131
+ * }} api
132
+ * @param {number} timeoutMs
133
+ */
134
+ async function waitForVisibleLazyData(api, timeoutMs) {
135
+ const controller = new AbortController();
136
+ let timedOut = false;
137
+ const timeoutId = window.setTimeout(() => {
138
+ timedOut = true;
139
+ controller.abort();
140
+ }, timeoutMs);
141
+
142
+ try {
143
+ await api.awaitVisibleLazyData(controller.signal);
144
+ } catch (error) {
145
+ if (timedOut) {
146
+ throw new Error(
147
+ `Timed out after ${timeoutMs} ms while waiting for visible lazy data.`
148
+ );
149
+ }
150
+
151
+ throw error;
152
+ } finally {
153
+ window.clearTimeout(timeoutId);
154
+ }
155
+ }
156
+
157
+ function waitForAnimationFrame() {
158
+ return new Promise((resolve) => requestAnimationFrame(() => resolve()));
159
+ }
160
+
161
+ /**
162
+ * @param {number} milliseconds
163
+ */
164
+ function wait(milliseconds) {
165
+ return new Promise((resolve) => window.setTimeout(resolve, milliseconds));
166
+ }
167
+
168
+ /**
169
+ * @param {string | null} value
170
+ * @param {number} fallback
171
+ */
172
+ function parseTimeoutMs(value, fallback) {
173
+ if (value == null) {
174
+ return fallback;
175
+ }
176
+
177
+ const parsed = Number(value);
178
+ if (!Number.isFinite(parsed) || parsed <= 0) {
179
+ throw new Error(
180
+ `Invalid lazy-data timeout value: ${value}. Expected a positive number.`
181
+ );
182
+ }
183
+
184
+ return parsed;
185
+ }
186
+
187
+ /**
188
+ * @param {{ width: number | undefined, height: number | undefined }} renderedBounds
189
+ * @param {{ width: number, height: number }} logicalSize
190
+ */
191
+ function resolveExportSize(renderedBounds, logicalSize) {
192
+ return {
193
+ width:
194
+ Number.isFinite(renderedBounds.width) && renderedBounds.width > 0
195
+ ? Math.ceil(renderedBounds.width)
196
+ : Number.isFinite(logicalSize.width) && logicalSize.width > 0
197
+ ? logicalSize.width
198
+ : DEFAULT_CONTAINER_WIDTH,
199
+ height:
200
+ Number.isFinite(renderedBounds.height) && renderedBounds.height > 0
201
+ ? Math.ceil(renderedBounds.height)
202
+ : Number.isFinite(logicalSize.height) && logicalSize.height > 0
203
+ ? logicalSize.height
204
+ : DEFAULT_CONTAINER_HEIGHT,
205
+ };
206
+ }
207
+
208
+ /**
209
+ * @param {string} message
210
+ */
211
+ function setStatus(message) {
212
+ statusElement.textContent = message;
213
+ }
214
+
215
+ /**
216
+ * @param {string} status
217
+ * @param {string} detail
218
+ */
219
+ function setState(status, detail) {
220
+ screenshotWindow.__genomeSpyScreenshot = {
221
+ ...screenshotWindow.__genomeSpyScreenshot,
222
+ status,
223
+ detail,
224
+ };
225
+ setStatus(detail);
226
+ }
227
+
228
+ /**
229
+ * @param {string} message
230
+ */
231
+ function setFailure(message) {
232
+ screenshotWindow.__genomeSpyScreenshot = {
233
+ status: "error",
234
+ detail: message,
235
+ error: message,
236
+ async capture() {
237
+ throw new Error(message);
238
+ },
239
+ };
240
+ statusElement.textContent = message;
241
+ statusElement.style.color = "#a03018";
242
+ }
@@ -9,5 +9,5 @@ if (specUrl) {
9
9
  document.body.innerHTML = `
10
10
  <p style="color: firebrick">No 'spec' url parameter defined!</p>
11
11
  <p>Try this one from the "static" folder, for example:
12
- <a href="?spec=examples/first.json">examples/first.json</a></p>`;
12
+ <a href="?spec=examples/core/first.json">examples/core/first.json</a></p>`;
13
13
  }
@@ -42,6 +42,7 @@ export interface DataFormatBase {
42
42
  * Type of input data: `"json"`, `"csv"`, `"tsv"`, `"dsv"`.
43
43
  *
44
44
  * __Default value:__ The default format type is determined by the extension of the file URL.
45
+ * Compression suffixes such as `.gz` are ignored during inference.
45
46
  * If no extension is detected, `"json"` will be used by default.
46
47
  */
47
48
  type?: DataFormatType;
@@ -75,10 +76,25 @@ export interface JsonDataFormat extends DataFormatBase {
75
76
  property?: string;
76
77
  }
77
78
 
79
+ export interface BedDataFormat extends DataFormatBase {
80
+ type: "bed";
81
+ }
82
+
83
+ export interface BedpeDataFormat extends DataFormatBase {
84
+ type: "bedpe";
85
+
86
+ /**
87
+ * Optional ordered list of field names for headerless BEDPE input.
88
+ * If omitted, BEDPE fields are resolved from the default BEDPE column
89
+ * order or from a matching header row when present.
90
+ */
91
+ columns?: string[];
92
+ }
93
+
78
94
  /**
79
95
  * Other data format, such as `"fasta"`
80
96
  */
81
- export interface OtherDataFormat {
97
+ export interface OtherDataFormat extends DataFormatBase {
82
98
  type: string;
83
99
  }
84
100
 
@@ -86,6 +102,8 @@ export type DataFormat =
86
102
  | CsvDataFormat
87
103
  | DsvDataFormat
88
104
  | JsonDataFormat
105
+ | BedDataFormat
106
+ | BedpeDataFormat
89
107
  | OtherDataFormat;
90
108
 
91
109
  export type DataFormatType = "json" | "csv" | "tsv" | "dsv" | string;
@@ -129,7 +147,7 @@ export interface UrlList {
129
147
  /**
130
148
  * The format of the data in the list.
131
149
  * If the type is `"json"`, the list is expected to be an array of strings.
132
- * If the type is `"csv"` or `"tsv"`, the list is expected to be a table with a single column named `file`.
150
+ * If the type is `"csv"` or `"tsv"`, the list is expected to be a table with a single column named `url`.
133
151
  *
134
152
  * __Default value:__ `"txt"`
135
153
  */
@@ -139,7 +157,9 @@ export interface UrlList {
139
157
  export interface UrlData extends DataBase {
140
158
  /**
141
159
  * An URL or an array of URLs from which to load the data set.
142
- * Use the `format.type` property to ensure the loaded data is correctly parsed.
160
+ * Gzip-compressed resources are decompressed transparently when the URL,
161
+ * MIME type, or payload indicates gzip content. Use the `format.type`
162
+ * property to ensure the loaded data is correctly parsed.
143
163
  */
144
164
  url: string | string[] | ExprRef | UrlList;
145
165
  }
@@ -10,7 +10,7 @@ export interface GenomeConfigBase {
10
10
  name: string;
11
11
  }
12
12
 
13
- export interface UrlGenomeConfig extends GenomeConfigBase {
13
+ export interface UrlGenomeDefinition {
14
14
  /**
15
15
  * A URL to a `chrom.sizes` file, which is a tab-separated file with two
16
16
  * columns: the sequence name and its size.
@@ -23,13 +23,33 @@ export interface UrlGenomeConfig extends GenomeConfigBase {
23
23
  url: string;
24
24
  }
25
25
 
26
- export interface InlineGenomeConfig extends GenomeConfigBase {
26
+ export interface InlineGenomeDefinition {
27
27
  /**
28
28
  * An array of contigs/sequences in the genome assembly.
29
29
  */
30
30
  contigs: Contig[];
31
31
  }
32
32
 
33
+ /**
34
+ * Genome definition for contexts where the name is provided externally
35
+ * (for example, as a key in root `genomes`) or not needed
36
+ * (for example, inline `scale.assembly`).
37
+ */
38
+ export type GenomeDefinition = UrlGenomeDefinition | InlineGenomeDefinition;
39
+
40
+ /**
41
+ * @deprecated Use `GenomeDefinition` in root `genomes` entries and `scale.assembly`.
42
+ */
43
+ export type UrlGenomeConfig = GenomeConfigBase & UrlGenomeDefinition;
44
+
45
+ /**
46
+ * @deprecated Use `GenomeDefinition` in root `genomes` entries and `scale.assembly`.
47
+ */
48
+ export type InlineGenomeConfig = GenomeConfigBase & InlineGenomeDefinition;
49
+
50
+ /**
51
+ * @deprecated Use root `genomes` and `assembly` instead of root `genome`.
52
+ */
33
53
  export type GenomeConfig =
34
54
  | UrlGenomeConfig
35
55
  | InlineGenomeConfig
@@ -177,10 +177,21 @@ export type SelectionInitInterval =
177
177
  | Vector2<number>
178
178
  | Vector2<string>;
179
179
 
180
- export type InteractionEventType = "click" | "dblclick" | "mouseover";
180
+ export type InteractionEventType =
181
+ | "click"
182
+ | "dblclick"
183
+ | "mouseover"
184
+ | "mousedown"
185
+ | "wheel";
181
186
 
182
187
  // TODO: merge with InteractionEventType
183
- export type DomEventType = "click" | "dblclick" | "mouseover" | "pointerover";
188
+ export type DomEventType =
189
+ | "click"
190
+ | "dblclick"
191
+ | "mouseover"
192
+ | "pointerover"
193
+ | "mousedown"
194
+ | "wheel";
184
195
 
185
196
  export interface EventConfig {
186
197
  /**
@@ -208,6 +219,13 @@ export interface BaseSelectionConfig<T extends SelectionType = SelectionType> {
208
219
 
209
220
  /**
210
221
  * A string or object that defines the events to which the selection should listen.
222
+ *
223
+ * __Default value:__
224
+ *
225
+ * - point selections: `"click"`
226
+ * - interval selections:
227
+ * - `"mousedown[event.shiftKey]"` when any brushed channel is zoomable
228
+ * - `"mousedown"` otherwise
211
229
  */
212
230
  on?: DomEventType | EventConfig | string;
213
231
 
@@ -247,6 +265,25 @@ export interface IntervalSelectionConfig extends BaseSelectionConfig<"interval">
247
265
  * Use the `mark` property to adjust the appearance of this rectangle.
248
266
  */
249
267
  mark?: BrushConfig;
268
+
269
+ /**
270
+ * Controls whether an active interval selection can be resized by mouse wheel.
271
+ * The wheel interaction only applies when the cursor is over the interval.
272
+ *
273
+ * Can be:
274
+ *
275
+ * - `true` / `false`
276
+ * - event type string such as `"wheel"` or `"wheel[event.altKey]"`
277
+ * - an `EventConfig` object
278
+ *
279
+ * Currently, only `"wheel"` events are supported.
280
+ *
281
+ * __Default value:__
282
+ *
283
+ * - `false` when any brushed channel uses a zoomable scale
284
+ * - `true` otherwise
285
+ */
286
+ zoom?: DomEventType | EventConfig | string | boolean;
250
287
  }
251
288
 
252
289
  export interface BrushConfig extends ShadowProps {
@@ -1,9 +1,28 @@
1
- import { GenomeConfig } from "./genome.js";
1
+ import { GenomeConfig, GenomeDefinition } from "./genome.js";
2
2
  import { ViewSpec } from "./view.js";
3
3
 
4
+ export type NamedGenomeConfig = GenomeDefinition | Record<string, never>;
5
+
4
6
  export interface RootConfig {
5
7
  $schema?: string;
6
8
 
9
+ /**
10
+ * Named genome assembly definitions.
11
+ *
12
+ * Each object key is the assembly name.
13
+ */
14
+ genomes?: Record<string, NamedGenomeConfig>;
15
+
16
+ /**
17
+ * Default assembly for locus scales that do not define `scale.assembly`.
18
+ *
19
+ * Can reference either a key in `genomes` or a built-in assembly name.
20
+ */
21
+ assembly?: string;
22
+
23
+ /**
24
+ * @deprecated Legacy root-level genome config. Use `genomes` and `assembly` instead.
25
+ */
7
26
  genome?: GenomeConfig;
8
27
 
9
28
  baseUrl?: string;
@@ -8,7 +8,7 @@
8
8
  * BSD-3-Clause License: https://github.com/vega/vega-lite/blob/master/LICENSE
9
9
  */
10
10
 
11
- import { ChromosomalLocus } from "./genome.js";
11
+ import { ChromosomalLocus, GenomeDefinition } from "./genome.js";
12
12
  import { ExprRef } from "./parameter.js";
13
13
 
14
14
  export type ScaleType =
@@ -48,10 +48,16 @@ export interface Scale {
48
48
  type?: ScaleType;
49
49
 
50
50
  /**
51
- * The genome assembly name for locus scales. If undefined, the default
52
- * genome from the genome store is used.
51
+ * Genome assembly definition for locus scales.
52
+ *
53
+ * This can be:
54
+ *
55
+ * - A string reference to a named assembly (built-in or root-configured).
56
+ * - An inline anonymous assembly that defines either `contigs` or `url`.
57
+ *
58
+ * If undefined, the default genome from the genome store is used.
53
59
  */
54
- assembly?: string;
60
+ assembly?: string | InlineLocusAssembly;
55
61
 
56
62
  /**
57
63
  * Customized domain values.
@@ -62,7 +68,7 @@ export interface Scale {
62
68
  *
63
69
  * For _ordinal_ and _nominal_ fields, `domain` can be an array that lists valid input values.
64
70
  */
65
- domain?: ScalarDomain | ComplexDomain;
71
+ domain?: ScalarDomain | ComplexDomain | SelectionDomainRef;
66
72
 
67
73
  /**
68
74
  * Inserts a single mid-point value into a two-element domain. The mid-point value must lie between the domain minimum and maximum values. This property can be useful for setting a midpoint for [diverging color scales](https://vega.github.io/vega-lite/docs/scale.html#piecewise). The domainMid property is only intended for use with scales supporting continuous, piecewise domains.
@@ -230,6 +236,36 @@ export interface Scale {
230
236
  zoom?: boolean | ZoomParams;
231
237
  }
232
238
 
239
+ export type InlineLocusAssembly = GenomeDefinition;
240
+
241
+ export interface SelectionDomainRef {
242
+ /**
243
+ * Name of an interval selection parameter that provides the domain.
244
+ */
245
+ param: string;
246
+
247
+ /**
248
+ * Selection interval channel to use.
249
+ *
250
+ * If omitted, GenomeSpy infers the channel from the scale channel when
251
+ * possible (e.g., `x` -> `x`, `x2` -> `x`, `y` -> `y`, `y2` -> `y`).
252
+ */
253
+ encoding?: "x" | "y";
254
+
255
+ /**
256
+ * Domain synchronization mode.
257
+ *
258
+ * - `"oneWay"`: selection drives the domain.
259
+ * - `"twoWay"`: selection drives the domain and zoom/pan updates selection.
260
+ *
261
+ * __Default value:__ implicit auto mode:
262
+ *
263
+ * - `"twoWay"` when the linked scale is zoomable
264
+ * - `"oneWay"` otherwise
265
+ */
266
+ sync?: "oneWay" | "twoWay";
267
+ }
268
+
233
269
  export interface SchemeParams {
234
270
  /**
235
271
  * A color scheme name for ordinal scales (e.g., `"category10"` or `"blues"`).
@@ -21,6 +21,7 @@
21
21
 
22
22
  canvas {
23
23
  display: block;
24
+ touch-action: none;
24
25
  transform: scale(1, 1);
25
26
  opacity: 1;
26
27
  transition:
@@ -163,6 +164,13 @@
163
164
  box-shadow: 0px 0px 3px 1px white;
164
165
  }
165
166
 
167
+ .color-legend-unmapped {
168
+ background-color: transparent;
169
+ border: 1px solid black;
170
+ box-sizing: border-box;
171
+ box-shadow: none;
172
+ }
173
+
166
174
  .attributes {
167
175
  .hovered {
168
176
  background-color: #e0e0e0;
@@ -1,3 +1,3 @@
1
1
  export default css;
2
- declare const css: "\n:root {\n--genome-spy-basic-spacing: 10px;\n--genome-spy-font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial,\nsans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n.genome-spy {\nfont-family: var(--genome-spy-font-family);\n\nposition: relative;\n\ndisplay: flex;\nflex-direction: column;\n\n.canvas-wrapper {\nposition: relative;\nflex-grow: 1;\noverflow: hidden;\n}\n\ncanvas {\ndisplay: block;\ntransform: scale(1, 1);\nopacity: 1;\ntransition:\ntransform 0.6s,\nopacity 0.6s;\n\n&:focus,\n&:focus-visible {\noutline: none;\n}\n}\n\n.loading {\n> canvas {\ntransform: scale(0.95, 0.95);\nopacity: 0;\n}\n}\n\n.loading-indicators {\nposition: absolute;\ninset: 0;\n\nuser-select: none;\npointer-events: none;\n\ndiv {\nposition: absolute;\ndisplay: flex;\nalign-items: center;\njustify-content: center;\n\n> div {\nfont-size: 11px;\ntransition: opacity 0.2s;\nbackground: white;\npadding: 2px 5px;\ndisplay: flex;\nborder-radius: 3px;\ngap: 0.5em;\nopacity: 0;\n\n&.loading {\nopacity: 0.5;\n}\n\n&.error {\nopacity: 0.8;\ncolor: firebrick;\n}\n\n> * {\ndisplay: block;\n}\n\nimg {\nwidth: 1.5em;\nheight: 1.5em;\n}\n}\n}\n}\n\n.tooltip {\nposition: absolute;\n\nmax-width: 450px;\noverflow: hidden;\n\n--background-color: #f6f6f6;\nbackground: var(--background-color);\npadding: var(--genome-spy-basic-spacing);\n\n--font-size: 12px;\nfont-size: var(--font-size);\n\nbox-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n\n&:not(.sticky) {\npointer-events: none;\n}\n\ntransition:\noutline-color 0.3s ease-in-out,\nbox-shadow 0.3s ease-in-out;\n\noutline: 0px solid transparent;\n&.sticky {\noutline: 2px solid black;\nbox-shadow: 0px 3px 18px 0px rgba(0, 0, 0, 0.3);\n}\n\nz-index: 100;\n\n> :last-child {\nmargin-bottom: 0;\n}\n\n> .title {\npadding-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nmargin-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nborder-bottom: 1px dashed var(--background-color);\nborder-bottom: 1px dashed\ncolor-mix(in srgb, black 25%, var(--background-color));\n}\n\n.summary {\nfont-size: 12px;\n}\n\ntable {\n&:first-child {\nmargin-top: 0;\n}\n\nborder-collapse: collapse;\n\nth,\ntd {\npadding: 2px 0.4em;\nvertical-align: top;\nfont-size: var(--font-size);\n\n&:first-child {\npadding-left: 0;\n}\n}\n\nth {\ntext-align: left;\nfont-weight: bold;\n}\n}\n\n.color-legend {\ndisplay: inline-block;\nwidth: 0.8em;\nheight: 0.8em;\nmargin-left: 0.4em;\nbox-shadow: 0px 0px 3px 1px white;\n}\n\n.attributes {\n.hovered {\nbackground-color: #e0e0e0;\n}\n}\n\n.na {\ncolor: #aaa;\nfont-style: italic;\nfont-size: 80%;\n}\n}\n\n.gene-track-tooltip {\n.summary {\nfont-size: 90%;\n}\n}\n\n.message-box {\ndisplay: flex;\nalign-items: center;\njustify-content: center;\nposition: absolute;\ntop: 0;\nheight: 100%;\nwidth: 100%;\n\n> div {\nborder: 1px solid red;\npadding: 10px;\nbackground: #fff0f0;\n}\n}\n}\n\n.gs-input-binding {\ndisplay: grid;\ngrid-template-columns: max-content max-content;\ncolumn-gap: 1em;\nrow-gap: 0.3em;\njustify-items: start;\n\n> select,\n> input:not([type=\"checkbox\"]) {\nwidth: 100%;\n}\n\ninput[type=\"range\"] + span {\ndisplay: inline-block;\nmargin-left: 0.3em;\nmin-width: 2.2em;\nfont-variant-numeric: tabular-nums;\n}\n\ninput[type=\"range\"],\ninput[type=\"radio\"] {\nvertical-align: text-bottom;\n}\n\n.radio-group {\ndisplay: flex;\nalign-items: center;\n}\n\n.description {\nmax-width: 26em;\ngrid-column: 1 / -1;\ncolor: #777;\nfont-size: 90%;\nmargin-top: -0.5em;\n}\n}\n\n.gs-input-bindings {\nflex-basis: content;\nfont-size: 14px;\npadding: var(--genome-spy-basic-spacing);\n}\n";
2
+ declare const css: "\n:root {\n--genome-spy-basic-spacing: 10px;\n--genome-spy-font-family: system-ui, \"Segoe UI\", Roboto, Helvetica, Arial,\nsans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n}\n\n.genome-spy {\nfont-family: var(--genome-spy-font-family);\n\nposition: relative;\n\ndisplay: flex;\nflex-direction: column;\n\n.canvas-wrapper {\nposition: relative;\nflex-grow: 1;\noverflow: hidden;\n}\n\ncanvas {\ndisplay: block;\ntouch-action: none;\ntransform: scale(1, 1);\nopacity: 1;\ntransition:\ntransform 0.6s,\nopacity 0.6s;\n\n&:focus,\n&:focus-visible {\noutline: none;\n}\n}\n\n.loading {\n> canvas {\ntransform: scale(0.95, 0.95);\nopacity: 0;\n}\n}\n\n.loading-indicators {\nposition: absolute;\ninset: 0;\n\nuser-select: none;\npointer-events: none;\n\ndiv {\nposition: absolute;\ndisplay: flex;\nalign-items: center;\njustify-content: center;\n\n> div {\nfont-size: 11px;\ntransition: opacity 0.2s;\nbackground: white;\npadding: 2px 5px;\ndisplay: flex;\nborder-radius: 3px;\ngap: 0.5em;\nopacity: 0;\n\n&.loading {\nopacity: 0.5;\n}\n\n&.error {\nopacity: 0.8;\ncolor: firebrick;\n}\n\n> * {\ndisplay: block;\n}\n\nimg {\nwidth: 1.5em;\nheight: 1.5em;\n}\n}\n}\n}\n\n.tooltip {\nposition: absolute;\n\nmax-width: 450px;\noverflow: hidden;\n\n--background-color: #f6f6f6;\nbackground: var(--background-color);\npadding: var(--genome-spy-basic-spacing);\n\n--font-size: 12px;\nfont-size: var(--font-size);\n\nbox-shadow: 0px 3px 15px 0px rgba(0, 0, 0, 0.21);\n\n&:not(.sticky) {\npointer-events: none;\n}\n\ntransition:\noutline-color 0.3s ease-in-out,\nbox-shadow 0.3s ease-in-out;\n\noutline: 0px solid transparent;\n&.sticky {\noutline: 2px solid black;\nbox-shadow: 0px 3px 18px 0px rgba(0, 0, 0, 0.3);\n}\n\nz-index: 100;\n\n> :last-child {\nmargin-bottom: 0;\n}\n\n> .title {\npadding-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nmargin-bottom: calc(var(--genome-spy-basic-spacing) / 2);\nborder-bottom: 1px dashed var(--background-color);\nborder-bottom: 1px dashed\ncolor-mix(in srgb, black 25%, var(--background-color));\n}\n\n.summary {\nfont-size: 12px;\n}\n\ntable {\n&:first-child {\nmargin-top: 0;\n}\n\nborder-collapse: collapse;\n\nth,\ntd {\npadding: 2px 0.4em;\nvertical-align: top;\nfont-size: var(--font-size);\n\n&:first-child {\npadding-left: 0;\n}\n}\n\nth {\ntext-align: left;\nfont-weight: bold;\n}\n}\n\n.color-legend {\ndisplay: inline-block;\nwidth: 0.8em;\nheight: 0.8em;\nmargin-left: 0.4em;\nbox-shadow: 0px 0px 3px 1px white;\n}\n\n.color-legend-unmapped {\nbackground-color: transparent;\nborder: 1px solid black;\nbox-sizing: border-box;\nbox-shadow: none;\n}\n\n.attributes {\n.hovered {\nbackground-color: #e0e0e0;\n}\n}\n\n.na {\ncolor: #aaa;\nfont-style: italic;\nfont-size: 80%;\n}\n}\n\n.gene-track-tooltip {\n.summary {\nfont-size: 90%;\n}\n}\n\n.message-box {\ndisplay: flex;\nalign-items: center;\njustify-content: center;\nposition: absolute;\ntop: 0;\nheight: 100%;\nwidth: 100%;\n\n> div {\nborder: 1px solid red;\npadding: 10px;\nbackground: #fff0f0;\n}\n}\n}\n\n.gs-input-binding {\ndisplay: grid;\ngrid-template-columns: max-content max-content;\ncolumn-gap: 1em;\nrow-gap: 0.3em;\njustify-items: start;\n\n> select,\n> input:not([type=\"checkbox\"]) {\nwidth: 100%;\n}\n\ninput[type=\"range\"] + span {\ndisplay: inline-block;\nmargin-left: 0.3em;\nmin-width: 2.2em;\nfont-variant-numeric: tabular-nums;\n}\n\ninput[type=\"range\"],\ninput[type=\"radio\"] {\nvertical-align: text-bottom;\n}\n\n.radio-group {\ndisplay: flex;\nalign-items: center;\n}\n\n.description {\nmax-width: 26em;\ngrid-column: 1 / -1;\ncolor: #777;\nfont-size: 90%;\nmargin-top: -0.5em;\n}\n}\n\n.gs-input-bindings {\nflex-basis: content;\nfont-size: 14px;\npadding: var(--genome-spy-basic-spacing);\n}\n";
3
3
  //# sourceMappingURL=genome-spy.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,0iHAoPE"}
1
+ {"version":3,"file":"genome-spy.css.d.ts","sourceRoot":"","sources":["../../../src/styles/genome-spy.css.js"],"names":[],"mappings":";AAAA,osHA4PE"}