jbrowse-plugin-mafviewer 1.2.2 → 1.2.4

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 (78) hide show
  1. package/README.md +6 -1
  2. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.d.ts +12 -3
  3. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js +123 -94
  4. package/dist/BgzipTaffyAdapter/BgzipTaffyAdapter.js.map +1 -1
  5. package/dist/BigMafAdapter/BigMafAdapter.d.ts +1 -1
  6. package/dist/BigMafAdapter/BigMafAdapter.js +50 -49
  7. package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
  8. package/dist/LinearMafDisplay/components/ColorLegend.d.ts +2 -4
  9. package/dist/LinearMafDisplay/components/ColorLegend.js +2 -3
  10. package/dist/LinearMafDisplay/components/ColorLegend.js.map +1 -1
  11. package/dist/LinearMafDisplay/components/{ReactComponent.d.ts → LinearMafDisplayComponent.d.ts} +1 -1
  12. package/dist/LinearMafDisplay/components/{ReactComponent.js → LinearMafDisplayComponent.js} +2 -2
  13. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -0
  14. package/dist/LinearMafDisplay/components/SetRowHeightDialog.js +38 -0
  15. package/dist/LinearMafDisplay/components/SetRowHeightDialog.js.map +1 -0
  16. package/dist/LinearMafDisplay/components/SvgWrapper.d.ts +1 -1
  17. package/dist/LinearMafDisplay/components/SvgWrapper.js.map +1 -1
  18. package/dist/LinearMafDisplay/components/Tree.d.ts +2 -1
  19. package/dist/LinearMafDisplay/components/Tree.js +2 -0
  20. package/dist/LinearMafDisplay/components/Tree.js.map +1 -1
  21. package/dist/LinearMafDisplay/components/YScaleBars.d.ts +1 -1
  22. package/dist/LinearMafDisplay/components/YScaleBars.js +1 -10
  23. package/dist/LinearMafDisplay/components/YScaleBars.js.map +1 -1
  24. package/dist/LinearMafDisplay/index.js +1 -1
  25. package/dist/LinearMafDisplay/index.js.map +1 -1
  26. package/dist/LinearMafDisplay/stateModel.d.ts +20 -19
  27. package/dist/LinearMafDisplay/stateModel.js +42 -7
  28. package/dist/LinearMafDisplay/stateModel.js.map +1 -1
  29. package/dist/LinearMafDisplay/types.d.ts +5 -3
  30. package/dist/LinearMafDisplay/types.js +1 -15
  31. package/dist/LinearMafDisplay/types.js.map +1 -1
  32. package/dist/LinearMafDisplay/util.d.ts +4 -0
  33. package/dist/LinearMafDisplay/util.js +16 -0
  34. package/dist/LinearMafDisplay/util.js.map +1 -0
  35. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +4 -4
  36. package/dist/LinearMafRenderer/LinearMafRenderer.js +9 -11
  37. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
  38. package/dist/LinearMafRenderer/makeImageData.js +35 -17
  39. package/dist/LinearMafRenderer/makeImageData.js.map +1 -1
  40. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +1 -1
  41. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
  42. package/dist/MafAddTrackWorkflow/index.js +0 -1
  43. package/dist/MafAddTrackWorkflow/index.js.map +1 -1
  44. package/dist/MafTabixAdapter/MafTabixAdapter.d.ts +1 -1
  45. package/dist/MafTabixAdapter/MafTabixAdapter.js +6 -7
  46. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
  47. package/dist/MafTabixAdapter/configSchema.js +29 -1
  48. package/dist/MafTabixAdapter/configSchema.js.map +1 -1
  49. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +7 -19
  50. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
  51. package/dist/util.d.ts +2 -2
  52. package/dist/util.js +5 -1
  53. package/dist/util.js.map +1 -1
  54. package/package.json +11 -10
  55. package/src/BgzipTaffyAdapter/BgzipTaffyAdapter.ts +135 -99
  56. package/src/BigMafAdapter/BigMafAdapter.ts +52 -49
  57. package/src/LinearMafDisplay/components/ColorLegend.tsx +11 -7
  58. package/src/LinearMafDisplay/components/{ReactComponent.tsx → LinearMafDisplayComponent.tsx} +2 -2
  59. package/src/LinearMafDisplay/components/SetRowHeightDialog.tsx +83 -0
  60. package/src/LinearMafDisplay/components/SvgWrapper.tsx +1 -2
  61. package/src/LinearMafDisplay/components/Tree.tsx +5 -1
  62. package/src/LinearMafDisplay/components/YScaleBars.tsx +3 -21
  63. package/src/LinearMafDisplay/index.ts +1 -1
  64. package/src/LinearMafDisplay/stateModel.ts +49 -18
  65. package/src/LinearMafDisplay/types.ts +4 -24
  66. package/src/LinearMafDisplay/util.ts +27 -0
  67. package/src/LinearMafRenderer/LinearMafRenderer.ts +10 -15
  68. package/src/LinearMafRenderer/makeImageData.ts +48 -18
  69. package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +9 -11
  70. package/src/MafAddTrackWorkflow/index.ts +0 -1
  71. package/src/MafTabixAdapter/MafTabixAdapter.ts +9 -7
  72. package/src/MafTabixAdapter/configSchema.ts +29 -1
  73. package/src/util.ts +6 -2
  74. package/dist/LinearMafDisplay/components/ReactComponent.js.map +0 -1
  75. package/dist/LinearMafDisplay/components/SetRowHeight.js +0 -36
  76. package/dist/LinearMafDisplay/components/SetRowHeight.js.map +0 -1
  77. package/src/LinearMafDisplay/components/SetRowHeight.tsx +0 -83
  78. /package/dist/LinearMafDisplay/components/{SetRowHeight.d.ts → SetRowHeightDialog.d.ts} +0 -0
package/dist/util.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  export declare function normalize(r: string[] | {
2
2
  id: string;
3
- label: string;
3
+ label?: string;
4
4
  color?: string;
5
5
  }[]): {
6
6
  id: string;
7
- label: string;
7
+ label?: string;
8
8
  color?: string;
9
9
  }[];
package/dist/util.js CHANGED
@@ -3,7 +3,11 @@ function isStrs(array) {
3
3
  }
4
4
  export function normalize(r) {
5
5
  return isStrs(r)
6
- ? r.map(elt => ({ id: elt, label: elt, color: undefined }))
6
+ ? r.map(elt => ({
7
+ id: elt,
8
+ label: elt,
9
+ color: undefined,
10
+ }))
7
11
  : r;
8
12
  }
9
13
  //# sourceMappingURL=util.js.map
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,KAAgB;IAC9B,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,CAA6D;IAE7D,OAAO,MAAM,CAAC,CAAC,CAAC;QACd,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC,CAAA;AACP,CAAC"}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAC,KAAgB;IAC9B,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,CAA8D;IAE9D,OAAO,MAAM,CAAC,CAAC,CAAC;QACd,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACZ,EAAE,EAAE,GAAG;YACP,KAAK,EAAE,GAAG;YACV,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC,CAAA;AACP,CAAC"}
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.2",
2
+ "version": "1.2.4",
3
3
  "license": "MIT",
4
4
  "name": "jbrowse-plugin-mafviewer",
5
5
  "keywords": [
@@ -31,12 +31,12 @@
31
31
  "@jbrowse/core": "^3.0.1",
32
32
  "@jbrowse/plugin-data-management": "^3.0.1",
33
33
  "@jbrowse/plugin-linear-genome-view": "^3.0.1",
34
- "@mui/material": "^6.2.0",
35
- "@mui/system": "^6.2.0",
36
- "@mui/x-data-grid": "^7.2.0",
34
+ "@mui/material": "^7.0.1",
35
+ "@mui/system": "^7.0.1",
36
+ "@mui/x-data-grid": "^8.2.0",
37
37
  "@types/d3-array": "^3.2.1",
38
38
  "@types/d3-hierarchy": "^3.1.7",
39
- "@types/node": "^22.10.2",
39
+ "@types/node": "^22.15.16",
40
40
  "@types/react": "^19.0.1",
41
41
  "@typescript-eslint/eslint-plugin": "^8.18.0",
42
42
  "@typescript-eslint/parser": "^8.18.0",
@@ -46,28 +46,29 @@
46
46
  "eslint-plugin-import": "^2.31.0",
47
47
  "eslint-plugin-react": "^7.20.3",
48
48
  "eslint-plugin-react-hooks": "^5.1.0",
49
- "eslint-plugin-unicorn": "^56.0.1",
49
+ "eslint-plugin-unicorn": "^59.0.1",
50
50
  "mobx": "^6.0.0",
51
51
  "mobx-react": "^9.0.1",
52
52
  "mobx-state-tree": "^5.4.1",
53
53
  "prettier": "^3.4.2",
54
- "pretty-bytes": "^6.1.1",
54
+ "pretty-bytes": "^7.0.0",
55
55
  "react": "^19.0.0",
56
56
  "react-dom": "^19.0.0",
57
57
  "rimraf": "^6.0.1",
58
58
  "rxjs": "^7.8.1",
59
59
  "serve": "^14.2.0",
60
- "tss-react": "^4.8.6",
60
+ "tss-react": "^4.9.18",
61
61
  "typescript": "^5.1.6",
62
62
  "typescript-eslint": "^8.18.0"
63
63
  },
64
64
  "dependencies": {
65
- "@gmod/bgzf-filehandle": "^2.0.4",
65
+ "@gmod/bgzf-filehandle": "^3.0.2",
66
+ "abortable-promise-cache": "^1.5.0",
66
67
  "buffer": "^6.0.3",
67
68
  "d3-array": "^3.2.4",
68
69
  "d3-hierarchy": "^3.1.2",
69
70
  "fast-deep-equal": "^3.1.3",
70
- "generic-filehandle2": "^1.0.0",
71
+ "generic-filehandle2": "^2.0.1",
71
72
  "long": "^5.2.3"
72
73
  }
73
74
  }
@@ -9,8 +9,10 @@ import {
9
9
  SimpleFeature,
10
10
  updateStatus,
11
11
  } from '@jbrowse/core/util'
12
+ import QuickLRU from '@jbrowse/core/util/QuickLRU'
12
13
  import { openLocation } from '@jbrowse/core/util/io'
13
14
  import { ObservableCreate } from '@jbrowse/core/util/rxjs'
15
+ import AbortablePromiseCache from 'abortable-promise-cache'
14
16
  import Long from 'long'
15
17
 
16
18
  import VirtualOffset from './virtualOffset'
@@ -20,6 +22,7 @@ import { parseRowInstructions } from './rowInstructions'
20
22
  import { parseLineByLine } from './util'
21
23
 
22
24
  import type { IndexData, OrganismRecord } from './types'
25
+
23
26
  interface Entry {
24
27
  type: string
25
28
  row: number
@@ -35,11 +38,125 @@ const toP = (s = 0) => +(+s).toFixed(1)
35
38
  export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
36
39
  public setupP?: Promise<IndexData>
37
40
 
41
+ private cache = new AbortablePromiseCache({
42
+ cache: new QuickLRU({ maxSize: 50 }),
43
+ // @ts-expect-error
44
+ fill: async ({ nextEntry, firstEntry }, signal, statusCallback) => {
45
+ const file = openLocation(this.getConf('tafGzLocation'))
46
+ const response = await file.read(
47
+ nextEntry.virtualOffset.blockPosition -
48
+ firstEntry.virtualOffset.blockPosition,
49
+ firstEntry.virtualOffset.blockPosition,
50
+ )
51
+ const buffer = await unzip(response)
52
+ const slice = buffer.slice(firstEntry.virtualOffset.dataPosition)
53
+ return this.getChunk(slice, {
54
+ statusCallback: statusCallback as (arg: string) => void,
55
+ signal,
56
+ })
57
+ },
58
+ })
59
+
38
60
  async getRefNames() {
39
61
  const data = await this.setup()
40
62
  return Object.keys(data)
41
63
  }
42
64
 
65
+ async getChunk(buffer: Uint8Array, opts?: BaseOptions) {
66
+ const { statusCallback = () => {} } = opts || {}
67
+ const alignments = {} as Record<string, OrganismRecord>
68
+ const data = [] as Entry[]
69
+ let a0: any
70
+ let j = 0
71
+ let b = 0
72
+ parseLineByLine(buffer, line => {
73
+ if (j++ % 100 === 0) {
74
+ statusCallback(
75
+ `Processing ${toP(b / 1_000_000)}/${toP(buffer.length / 1_000_000)}Mb`,
76
+ )
77
+ }
78
+ b += line.length
79
+ if (line) {
80
+ const [lineData, rowInstructions] = line.split(' ; ')
81
+ if (rowInstructions) {
82
+ for (const ins of parseRowInstructions(rowInstructions)) {
83
+ if (ins.type === 'i') {
84
+ data.splice(ins.row, 0, ins)
85
+ if (!alignments[ins.asm]) {
86
+ alignments[ins.asm] = {
87
+ start: ins.start,
88
+ strand: ins.strand,
89
+ srcSize: ins.length,
90
+ chr: ins.ref,
91
+ data: '',
92
+ }
93
+ }
94
+ const e = alignments[ins.asm]!
95
+ e.data += ' '.repeat(Math.max(0, j - e.data.length - 1)) // catch it up
96
+ } else if (ins.type === 's') {
97
+ if (!alignments[ins.asm]) {
98
+ alignments[ins.asm] = {
99
+ start: ins.start,
100
+ strand: ins.strand,
101
+ srcSize: ins.length,
102
+ chr: ins.ref,
103
+ data: '',
104
+ }
105
+ }
106
+ const e = alignments[ins.asm]!
107
+ e.data += ' '.repeat(Math.max(0, j - e.data.length - 1)) // catch it up
108
+ data[ins.row] = ins
109
+ } else if (ins.type === 'd') {
110
+ data.splice(ins.row, 1)
111
+ }
112
+
113
+ // no gaps for now(?)
114
+ // else if (ins.type === 'g') {
115
+ // console.log('g??')
116
+ // } else if (ins.type === 'G') {
117
+ // console.log('G??')
118
+ // }
119
+ }
120
+ if (!a0) {
121
+ a0 = data[0]
122
+ }
123
+ }
124
+ const lineLen = lineData!.length
125
+
126
+ for (let i = 0; i < lineLen; i++) {
127
+ const letter = lineData![i]
128
+ const r = data[i]
129
+
130
+ if (r) {
131
+ alignments[r.asm]!.data += letter
132
+ } else {
133
+ // not sure why but chr22_KI270731v1_random.taf.gz ends up here
134
+ }
135
+ }
136
+ }
137
+ })
138
+ if (a0) {
139
+ const row0 = alignments[a0.asm]!
140
+
141
+ // see
142
+ // https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
143
+ // for the significance of row[0]:
144
+ //
145
+ // "An anchor line in TAF is a column from which all sequence
146
+ // coordinates can be deduced without scanning backwards to previous
147
+ // lines "
148
+ return {
149
+ uniqueId: `${row0.start}-${row0.data.length}`,
150
+ start: row0.start,
151
+ end: row0.start + row0.data.length,
152
+ strand: row0.strand,
153
+ alignments,
154
+ seq: row0.data,
155
+ }
156
+ }
157
+ return undefined
158
+ }
159
+
43
160
  setupPre() {
44
161
  if (!this.setupP) {
45
162
  this.setupP = this.readTaiFile().catch((e: unknown) => {
@@ -104,103 +221,23 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
104
221
  return ObservableCreate<Feature>(async observer => {
105
222
  try {
106
223
  const byteRanges = await this.setup()
107
- const buffer = await updateStatus(
224
+ const feat = await updateStatus(
108
225
  'Downloading alignments',
109
226
  statusCallback,
110
227
  () => this.getLines(query, byteRanges),
111
228
  )
112
- if (buffer) {
113
- const alignments = {} as Record<string, OrganismRecord>
114
- const data = [] as Entry[]
115
- let a0: any
116
- let j = 0
117
- let b = 0
118
- parseLineByLine(buffer, line => {
119
- if (j++ % 100 === 0) {
120
- statusCallback(
121
- `Processing ${toP(b / 1_000_000)}/${toP(buffer.length / 1_000_000)}Mb`,
122
- )
123
- }
124
- b += line.length
125
- if (line) {
126
- const [lineData, rowInstructions] = line.split(' ; ')
127
- if (rowInstructions) {
128
- for (const ins of parseRowInstructions(rowInstructions)) {
129
- if (ins.type === 'i') {
130
- data.splice(ins.row, 0, ins)
131
- if (!alignments[ins.asm]) {
132
- alignments[ins.asm] = {
133
- start: ins.start,
134
- strand: ins.strand,
135
- srcSize: ins.length,
136
- chr: ins.ref,
137
- data: '',
138
- }
139
- }
140
- const e = alignments[ins.asm]!
141
- e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
142
- } else if (ins.type === 's') {
143
- if (!alignments[ins.asm]) {
144
- alignments[ins.asm] = {
145
- start: ins.start,
146
- strand: ins.strand,
147
- srcSize: ins.length,
148
- chr: ins.ref,
149
- data: '',
150
- }
151
- }
152
- const e = alignments[ins.asm]!
153
- e.data += ' '.repeat(Math.max(0, j - e.data.length)) // catch it up
154
- data[ins.row] = ins
155
- } else if (ins.type === 'd') {
156
- data.splice(ins.row, 1)
157
- }
158
-
159
- // no gaps for now(?)
160
- // else if (ins.type === 'g') {
161
- // }
162
- // else if (ins.type === 'G') {
163
- // }
164
- }
165
- if (!a0) {
166
- a0 = data[0]
167
- }
168
- }
169
- const lineLen = lineData!.length
170
- for (let i = 0; i < lineLen; i++) {
171
- const letter = lineData![i]
172
- const r = data[i]
173
- if (r) {
174
- alignments[r.asm]!.data += letter
175
- } else {
176
- // not sure why but chr22_KI270731v1_random.taf.gz ends up here
177
- }
178
- }
179
- }
180
- })
181
- if (a0) {
182
- const row0 = alignments[a0.asm]!
183
-
184
- // see
185
- // https://github.com/ComparativeGenomicsToolkit/taffy/blob/f5a5354/docs/taffy_utilities.md#referenced-based-maftaf-and-indexing
186
- // for the significance of row[0]:
187
- //
188
- // "An anchor line in TAF is a column from which all sequence
189
- // coordinates can be deduced without scanning backwards to previous
190
- // lines "
191
- observer.next(
192
- new SimpleFeature({
193
- uniqueId: `${row0.start}-${row0.data.length}`,
194
- refName: query.refName,
195
- start: row0.start,
196
- end: row0.start + row0.data.length,
197
- strand: row0.strand,
198
- alignments,
199
- seq: row0.data,
200
- }),
201
- )
202
- }
229
+ if (feat) {
230
+ observer.next(
231
+ // @ts-expect-error
232
+ new SimpleFeature({
233
+ ...feat,
234
+ refName: query.refName,
235
+ }),
236
+ )
237
+ } else {
238
+ console.error('no feature found')
203
239
  }
240
+
204
241
  statusCallback('')
205
242
  observer.complete()
206
243
  } catch (e) {
@@ -226,7 +263,6 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
226
263
 
227
264
  // TODO: cache processed large chunks
228
265
  async getLines(query: Region, byteRanges: IndexData) {
229
- const file = openLocation(this.getConf('tafGzLocation'))
230
266
  const records = byteRanges[query.refName]
231
267
  if (records) {
232
268
  let firstEntry
@@ -255,13 +291,13 @@ export default class BgzipTaffyAdapter extends BaseFeatureDataAdapter {
255
291
  // file whn you request e.g. out of range region (e.g. taf in chr22:1-100
256
292
  // and you are at chr22:200-300)
257
293
  if (firstEntry && nextEntry) {
258
- const response = await file.read(
259
- nextEntry.virtualOffset.blockPosition -
260
- firstEntry.virtualOffset.blockPosition,
261
- firstEntry.virtualOffset.blockPosition,
294
+ return this.cache.get(
295
+ `${JSON.stringify(nextEntry)}_${JSON.stringify(firstEntry)}`,
296
+ {
297
+ nextEntry,
298
+ firstEntry,
299
+ },
262
300
  )
263
- const buffer = await unzip(response)
264
- return buffer.slice(firstEntry.virtualOffset.dataPosition)
265
301
  }
266
302
  }
267
303
  return undefined
@@ -26,12 +26,13 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
26
26
  if (!this.getSubAdapter) {
27
27
  throw new Error('no getSubAdapter available')
28
28
  }
29
- const adapter = await this.getSubAdapter({
30
- ...getSnapshot(this.config),
31
- type: 'BigBedAdapter',
32
- })
33
29
  return {
34
- adapter: adapter.dataAdapter as BaseFeatureDataAdapter,
30
+ adapter: (
31
+ await this.getSubAdapter({
32
+ ...getSnapshot(this.config),
33
+ type: 'BigBedAdapter',
34
+ })
35
+ ).dataAdapter as BaseFeatureDataAdapter,
35
36
  }
36
37
  }
37
38
  async setupPre() {
@@ -59,59 +60,61 @@ export default class BigMafAdapter extends BaseFeatureDataAdapter {
59
60
  return ObservableCreate<Feature>(async observer => {
60
61
  const { adapter } = await this.setup()
61
62
  const features = await updateStatus(
62
- 'Downloading alignment',
63
+ 'Downloading alignments',
63
64
  statusCallback,
64
65
  () => firstValueFrom(adapter.getFeatures(query).pipe(toArray())),
65
66
  )
66
- for (const feature of features) {
67
- const maf = feature.get('mafBlock') as string
68
- const blocks = maf.split(';')
69
- let aln: string | undefined
70
- const alns = [] as string[]
71
- const alignments = {} as Record<string, OrganismRecord>
72
- const blocks2 = [] as string[]
73
- for (const block of blocks) {
74
- if (block.startsWith('s')) {
75
- if (aln) {
76
- alns.push(block.split(/ +/)[6]!)
77
- blocks2.push(block)
78
- } else {
79
- aln = block.split(/ +/)[6]
80
- alns.push(aln!)
81
- blocks2.push(block)
67
+ await updateStatus('Processing alignments', statusCallback, () => {
68
+ for (const feature of features) {
69
+ const maf = feature.get('mafBlock') as string
70
+ const blocks = maf.split(';')
71
+ let aln: string | undefined
72
+ const alns = [] as string[]
73
+ const alignments = {} as Record<string, OrganismRecord>
74
+ const blocks2 = [] as string[]
75
+ for (const block of blocks) {
76
+ if (block.startsWith('s')) {
77
+ if (aln) {
78
+ alns.push(block.split(/ +/)[6]!)
79
+ blocks2.push(block)
80
+ } else {
81
+ aln = block.split(/ +/)[6]
82
+ alns.push(aln!)
83
+ blocks2.push(block)
84
+ }
82
85
  }
83
86
  }
84
- }
85
87
 
86
- for (let i = 0; i < blocks2.length; i++) {
87
- const elt = blocks2[i]!
88
- const ad = elt.split(/ +/)
89
- const y = ad[1]!.split('.')
90
- const org = y[0]!
91
- const chr = y[1]!
88
+ for (let i = 0; i < blocks2.length; i++) {
89
+ const elt = blocks2[i]!
90
+ const ad = elt.split(/ +/)
91
+ const y = ad[1]!.split('.')
92
+ const org = y[0]!
93
+ const chr = y[1]!
92
94
 
93
- alignments[org] = {
94
- chr: chr,
95
- start: +ad[1]!,
96
- srcSize: +ad[2]!,
97
- strand: ad[3] === '+' ? 1 : -1,
98
- unknown: +ad[4]!,
99
- data: alns[i]!,
95
+ alignments[org] = {
96
+ chr: chr,
97
+ start: +ad[1]!,
98
+ srcSize: +ad[2]!,
99
+ strand: ad[3] === '+' ? 1 : -1,
100
+ unknown: +ad[4]!,
101
+ data: alns[i]!,
102
+ }
100
103
  }
104
+ observer.next(
105
+ new SimpleFeature({
106
+ id: feature.id(),
107
+ data: {
108
+ start: feature.get('start'),
109
+ end: feature.get('end'),
110
+ refName: feature.get('refName'),
111
+ seq: alns[0],
112
+ alignments: alignments,
113
+ },
114
+ }),
115
+ )
101
116
  }
102
- observer.next(
103
- new SimpleFeature({
104
- id: feature.id(),
105
- data: {
106
- start: feature.get('start'),
107
- end: feature.get('end'),
108
- refName: feature.get('refName'),
109
- seq: alns[0],
110
- alignments: alignments,
111
- },
112
- }),
113
- )
114
- }
117
+ })
115
118
  observer.complete()
116
119
  })
117
120
  }
@@ -2,21 +2,25 @@ import React from 'react'
2
2
 
3
3
  import { observer } from 'mobx-react'
4
4
 
5
- import { LinearMafDisplayModel } from '../stateModel'
6
5
  import RectBg from './RectBg'
7
6
  import Tree from './Tree'
8
7
 
8
+ import type { LinearMafDisplayModel } from '../stateModel'
9
+
9
10
  const ColorLegend = observer(function ({
10
11
  model,
11
- labelWidth,
12
- svgFontSize,
13
12
  }: {
14
13
  model: LinearMafDisplayModel
15
- svgFontSize: number
16
- labelWidth: number
17
14
  }) {
18
- const { totalHeight, treeWidth, samples = [], rowHeight } = model
19
- const canDisplayLabel = rowHeight >= 8
15
+ const {
16
+ labelWidth,
17
+ canDisplayLabel,
18
+ totalHeight,
19
+ treeWidth,
20
+ samples = [],
21
+ rowHeight,
22
+ svgFontSize,
23
+ } = model
20
24
  const boxHeight = Math.min(20, rowHeight)
21
25
 
22
26
  return (
@@ -1,14 +1,14 @@
1
1
  import React, { useRef, useState } from 'react'
2
2
 
3
+ import { SanitizedHTML } from '@jbrowse/core/ui'
3
4
  import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
4
- import SanitizedHTML from '@jbrowse/core/ui/SanitizedHTML'
5
5
  import { getContainingView, getEnv } from '@jbrowse/core/util'
6
6
  import { observer } from 'mobx-react'
7
7
  import { makeStyles } from 'tss-react/mui'
8
8
 
9
9
  import YScaleBars from './YScaleBars'
10
- import { LinearMafDisplayModel } from '../stateModel'
11
10
 
11
+ import type { LinearMafDisplayModel } from '../stateModel'
12
12
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
13
13
 
14
14
  const useStyles = makeStyles()({
@@ -0,0 +1,83 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import { Dialog } from '@jbrowse/core/ui'
4
+ import {
5
+ Button,
6
+ DialogActions,
7
+ DialogContent,
8
+ TextField,
9
+ Typography,
10
+ } from '@mui/material'
11
+ import { observer } from 'mobx-react'
12
+ import { makeStyles } from 'tss-react/mui'
13
+
14
+ const useStyles = makeStyles()({
15
+ root: {
16
+ width: 500,
17
+ },
18
+ })
19
+
20
+ const SetRowHeightDialog = observer(function (props: {
21
+ model: {
22
+ rowHeight?: number
23
+ rowProportion?: number
24
+ setRowHeight: (arg: number) => void
25
+ setRowProportion: (arg: number) => void
26
+ }
27
+ handleClose: () => void
28
+ }) {
29
+ const { model, handleClose } = props
30
+ const { classes } = useStyles()
31
+ const [rowHeight, setRowHeight] = useState(`${model.rowHeight}`)
32
+ const [rowProportion, setRowProportion] = useState(`${model.rowProportion}`)
33
+
34
+ return (
35
+ <Dialog open onClose={handleClose} title="Set row height">
36
+ <form
37
+ onSubmit={event => {
38
+ event.preventDefault()
39
+ model.setRowProportion(+rowProportion)
40
+ model.setRowHeight(+rowHeight)
41
+ handleClose()
42
+ }}
43
+ >
44
+ <DialogContent className={classes.root}>
45
+ <Typography>
46
+ Set row height and the proportion of the row height to use for
47
+ drawing each row
48
+ </Typography>
49
+ <TextField
50
+ value={rowHeight}
51
+ helperText="Enter row height"
52
+ autoFocus
53
+ onChange={event => {
54
+ setRowHeight(event.target.value)
55
+ }}
56
+ />
57
+ <TextField
58
+ value={rowProportion}
59
+ helperText="Enter row proportion"
60
+ onChange={event => {
61
+ setRowProportion(event.target.value)
62
+ }}
63
+ />
64
+ <DialogActions>
65
+ <Button variant="contained" color="primary" type="submit">
66
+ Submit
67
+ </Button>
68
+ <Button
69
+ variant="contained"
70
+ color="secondary"
71
+ onClick={() => {
72
+ handleClose()
73
+ }}
74
+ >
75
+ Cancel
76
+ </Button>
77
+ </DialogActions>
78
+ </DialogContent>
79
+ </form>
80
+ </Dialog>
81
+ )
82
+ })
83
+ export default SetRowHeightDialog
@@ -3,8 +3,7 @@ import React from 'react'
3
3
  import { getContainingView } from '@jbrowse/core/util'
4
4
  import { observer } from 'mobx-react'
5
5
 
6
- // locals
7
- import { LinearMafDisplayModel } from '../stateModel'
6
+ import type { LinearMafDisplayModel } from '../stateModel'
8
7
 
9
8
  const SvgWrapper = observer(function ({
10
9
  children,
@@ -2,7 +2,9 @@ import React from 'react'
2
2
 
3
3
  import { observer } from 'mobx-react'
4
4
 
5
- const Tree = observer(function ({ model }: { model: any }) {
5
+ import type { LinearMafDisplayModel } from '../stateModel'
6
+
7
+ const Tree = observer(function ({ model }: { model: LinearMafDisplayModel }) {
6
8
  const {
7
9
  // this is needed for redrawing after zoom change, similar to react-msaview
8
10
  // renderTreeCanvas
@@ -20,7 +22,9 @@ const Tree = observer(function ({ model }: { model: any }) {
20
22
  const { source, target } = link
21
23
  const sy = source.x!
22
24
  const ty = target.x!
25
+ // @ts-expect-error
23
26
  const tx = showBranchLen ? target.len : target.y
27
+ // @ts-expect-error
24
28
  const sx = showBranchLen ? source.len : source.y
25
29
 
26
30
  // 1d line intersection to check if line crosses block at all, this is