jbrowse-plugin-msaview 2.6.3 → 2.6.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 (31) hide show
  1. package/dist/AddHighlightModel/MsaToGenomeHighlight.js +8 -9
  2. package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +0 -2
  3. package/dist/MsaViewPanel/afterCreateAutoruns.js +1 -63
  4. package/dist/MsaViewPanel/model.d.ts +3 -28
  5. package/dist/MsaViewPanel/model.js +5 -103
  6. package/dist/MsaViewPanel/structureConnection.d.ts +0 -38
  7. package/dist/MsaViewPanel/structureConnection.js +0 -20
  8. package/dist/MsaViewPanel/structureConnection.test.js +1 -35
  9. package/dist/jbrowse-plugin-msaview.umd.production.min.js +26 -26
  10. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  11. package/dist/version.d.ts +1 -1
  12. package/dist/version.js +1 -1
  13. package/package.json +1 -1
  14. package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +8 -9
  15. package/src/MsaViewPanel/afterCreateAutoruns.ts +1 -84
  16. package/src/MsaViewPanel/model.ts +4 -135
  17. package/src/MsaViewPanel/structureConnection.test.ts +1 -53
  18. package/src/MsaViewPanel/structureConnection.ts +0 -47
  19. package/src/version.ts +1 -1
  20. package/dist/MsaViewPanel/autoConnectStructures.test.d.ts +0 -1
  21. package/dist/MsaViewPanel/autoConnectStructures.test.js +0 -60
  22. package/dist/MsaViewPanel/components/ConnectStructureDialog.d.ts +0 -7
  23. package/dist/MsaViewPanel/components/ConnectStructureDialog.js +0 -60
  24. package/dist/MsaViewPanel/pairwiseAlignment.d.ts +0 -20
  25. package/dist/MsaViewPanel/pairwiseAlignment.js +0 -138
  26. package/dist/MsaViewPanel/pairwiseAlignment.test.d.ts +0 -1
  27. package/dist/MsaViewPanel/pairwiseAlignment.test.js +0 -111
  28. package/src/MsaViewPanel/autoConnectStructures.test.ts +0 -76
  29. package/src/MsaViewPanel/components/ConnectStructureDialog.tsx +0 -154
  30. package/src/MsaViewPanel/pairwiseAlignment.test.ts +0 -140
  31. package/src/MsaViewPanel/pairwiseAlignment.ts +0 -182
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "2.6.3";
1
+ export declare const version = "2.6.4";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = '2.6.3';
1
+ export const version = '2.6.4';
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.6.3",
2
+ "version": "2.6.4",
3
3
  "license": "MIT",
4
4
  "name": "jbrowse-plugin-msaview",
5
5
  "repository": {
@@ -5,7 +5,6 @@ import { observer } from 'mobx-react'
5
5
 
6
6
  import { hasHoverPosition, useStyles } from './util'
7
7
  import { isMsaView } from '../MsaViewPanel/model'
8
- import { getCanonicalRefName } from '../MsaViewPanel/util'
9
8
 
10
9
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
11
10
 
@@ -38,19 +37,19 @@ const MsaToGenomeHighlightRenderer = observer(function ({
38
37
  highlights: { refName: string; start: number; end: number }[]
39
38
  }) {
40
39
  const { classes } = useStyles()
41
- const { assemblyManager } = getSession(model)
42
40
  const { offsetPx } = model
43
41
 
44
42
  return (
45
43
  <>
46
44
  {highlights.map((r, idx) => {
47
- const refName = getCanonicalRefName({
48
- assemblyManager,
49
- assemblyNames: model.assemblyNames,
50
- refName: r.refName,
51
- })
52
- const s = model.bpToPx({ refName, coord: r.start })
53
- const e = model.bpToPx({ refName, coord: r.end })
45
+ // Use the highlight's own refName, which is already in the connected
46
+ // view's coordinate space (it comes from the connectedFeature the
47
+ // launcher set on this LGV). Do NOT canonicalize: bpToPx matches
48
+ // displayed regions by exact refName with no alias resolution, so
49
+ // rewriting e.g. "chr17" to the assembly-canonical "17" misses a view
50
+ // whose regions are "chr17". (GenomeMouseoverHighlight does the same.)
51
+ const s = model.bpToPx({ refName: r.refName, coord: r.start })
52
+ const e = model.bpToPx({ refName: r.refName, coord: r.end })
54
53
  if (s && e) {
55
54
  const width = Math.max(Math.abs(e.offsetPx - s.offsetPx), 4)
56
55
  const left = Math.min(s.offsetPx, e.offsetPx) - offsetPx
@@ -10,11 +10,7 @@ import {
10
10
  retrieveMsaData,
11
11
  storeMsaData,
12
12
  } from './msaDataStore'
13
- import {
14
- gappedToUngappedPosition,
15
- getProteinViews,
16
- structureMatchesMsa,
17
- } from './structureConnection'
13
+ import { getProteinViews } from './structureConnection'
18
14
  import { getUniprotIdFromAlphaFoldUrl } from './util'
19
15
 
20
16
  import type { JBrowsePluginMsaViewModel } from './model'
@@ -199,85 +195,6 @@ export function syncGenomeHoverToMsaColumn(self: JBrowsePluginMsaViewModel) {
199
195
  }
200
196
  }
201
197
 
202
- export function highlightConnectedStructures(self: JBrowsePluginMsaViewModel) {
203
- const { mouseCol, connectedProteinViews } = self
204
- if (connectedProteinViews.length === 0) {
205
- return
206
- }
207
-
208
- for (const conn of connectedProteinViews) {
209
- const structure = conn.proteinView.structures[conn.structureIdx]
210
- if (!structure) {
211
- continue
212
- }
213
-
214
- if (mouseCol === undefined) {
215
- structure.clearHighlightFromExternal?.()
216
- continue
217
- }
218
-
219
- const seq = self.getSequenceByRowName(conn.msaRowName)
220
- if (!seq) {
221
- continue
222
- }
223
-
224
- const msaUngapped = gappedToUngappedPosition(seq, mouseCol)
225
- if (msaUngapped === undefined) {
226
- structure.clearHighlightFromExternal?.()
227
- continue
228
- }
229
-
230
- const structurePos = conn.msaToStructure[msaUngapped]
231
- if (structurePos === undefined) {
232
- structure.clearHighlightFromExternal?.()
233
- } else {
234
- structure.highlightFromExternal?.(structurePos)
235
- }
236
- }
237
- }
238
-
239
- export function autoConnectStructures(self: JBrowsePluginMsaViewModel) {
240
- const { connectedViewId, uniprotId, rows, connectedStructures } = self
241
-
242
- if (rows.length === 0) {
243
- return
244
- }
245
-
246
- for (const view of getProteinViews(getSession(self).views)) {
247
- for (
248
- let structureIdx = 0;
249
- structureIdx < view.structures.length;
250
- structureIdx++
251
- ) {
252
- const structure = view.structures[structureIdx]
253
- if (!structure) {
254
- continue
255
- }
256
-
257
- if (!structureMatchesMsa({ structure, connectedViewId, uniprotId })) {
258
- continue
259
- }
260
-
261
- const alreadyConnected = connectedStructures.some(
262
- c => c.proteinViewId === view.id && c.structureIdx === structureIdx,
263
- )
264
- if (alreadyConnected) {
265
- continue
266
- }
267
-
268
- if (!structure.structureSequences?.[0]) {
269
- continue
270
- }
271
-
272
- try {
273
- self.connectToStructure(view.id, structureIdx)
274
- } catch (e) {
275
- console.error('Failed to auto-connect to ProteinView:', e)
276
- }
277
- }
278
- }
279
- }
280
-
281
198
  /**
282
199
  * Mirror a connected 3D protein view's hovered residue onto the MSA's
283
200
  * highlighted columns. Returns the autorun body and keeps a flag tracking
@@ -1,5 +1,3 @@
1
- import { lazy } from 'react'
2
-
3
1
  import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes'
4
2
  import { getSession } from '@jbrowse/core/util'
5
3
  import { addDisposer, types } from '@jbrowse/mobx-state-tree'
@@ -12,9 +10,7 @@ import { MSAModelF } from 'react-msaview'
12
10
  export type { MSAFormat } from 'msa-parsers'
13
11
 
14
12
  import {
15
- autoConnectStructures,
16
13
  autoLoadProteinDomains,
17
- highlightConnectedStructures,
18
14
  launchBlastIfNeeded,
19
15
  loadStoredData,
20
16
  observeProteinHighlights,
@@ -24,11 +20,7 @@ import {
24
20
  syncGenomeHoverToMsaColumn,
25
21
  } from './afterCreateAutoruns'
26
22
  import { msaCoordToGenomeCoord } from './msaCoordToGenomeCoord'
27
- import { buildAlignmentMaps, runPairwiseAlignment } from './pairwiseAlignment'
28
- import { getProteinViews } from './structureConnection'
29
- import { getCanonicalRefName } from './util'
30
23
 
31
- import type { ProteinView, StructureConnection } from './structureConnection'
32
24
  import type { MafRegion, MsaViewInitState } from './types'
33
25
  import type {
34
26
  BlastDatabase,
@@ -39,10 +31,6 @@ import type { Feature } from '@jbrowse/core/util'
39
31
  import type { Instance } from '@jbrowse/mobx-state-tree'
40
32
  import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
41
33
 
42
- const ConnectStructureDialog = lazy(
43
- () => import('./components/ConnectStructureDialog'),
44
- )
45
-
46
34
  type LGV = LinearGenomeViewModel
47
35
 
48
36
  type MaybeLGV = LGV | undefined
@@ -106,11 +94,6 @@ export default function stateModelFactory() {
106
94
  */
107
95
  init: types.frozen<MsaViewInitState | undefined>(),
108
96
 
109
- /**
110
- * #property
111
- */
112
- connectedStructures: types.array(types.frozen<StructureConnection>()),
113
-
114
97
  /**
115
98
  * #property
116
99
  */
@@ -194,24 +177,6 @@ export default function stateModelFactory() {
194
177
  const { views } = getSession(self)
195
178
  return views.find(f => f.id === self.connectedViewId) as MaybeLGV
196
179
  },
197
-
198
- /**
199
- * #getter
200
- */
201
- get connectedProteinViews() {
202
- const proteinViews = getProteinViews(getSession(self).views)
203
- const result: (StructureConnection & { proteinView: ProteinView })[] =
204
- []
205
- for (const conn of self.connectedStructures) {
206
- const proteinView = proteinViews.find(
207
- v => v.id === conn.proteinViewId,
208
- )
209
- if (proteinView) {
210
- result.push({ ...conn, proteinView })
211
- }
212
- }
213
- return result
214
- },
215
180
  }))
216
181
 
217
182
  .views(self => ({
@@ -315,94 +280,22 @@ export default function stateModelFactory() {
315
280
  */
316
281
  handleMsaClick(coord: number) {
317
282
  const { connectedView, zoomToBaseLevel } = self
318
- const { assemblyManager } = getSession(self)
319
283
  const r2 = msaCoordToGenomeCoord({ model: self, coord })
320
284
 
321
285
  if (!r2 || !connectedView) {
322
286
  return
323
287
  }
324
288
 
289
+ // Use the genome coord's own refName for both nav paths — it matches the
290
+ // connected view's displayed regions. Canonicalizing (e.g. "chr17"->"17")
291
+ // can miss a view whose regions are an alias (same as the bpToPx path).
325
292
  if (zoomToBaseLevel) {
326
293
  connectedView.navTo(r2)
327
294
  } else {
328
- const r = getCanonicalRefName({
329
- assemblyManager,
330
- assemblyNames: connectedView.assemblyNames,
331
- refName: r2.refName,
332
- })
333
- connectedView.centerAt(r2.start, r)
295
+ connectedView.centerAt(r2.start, r2.refName)
334
296
  }
335
297
  },
336
298
 
337
- /**
338
- * #action
339
- */
340
- connectToStructure(
341
- proteinViewId: string,
342
- structureIdx: number,
343
- msaRowName?: string,
344
- ) {
345
- const rowName = msaRowName ?? self.querySeqName
346
- const msaSequence = self.getSequenceByRowName(rowName)
347
- if (!msaSequence) {
348
- throw new Error(`MSA row "${rowName}" not found`)
349
- }
350
-
351
- const ungappedMsaSequence = msaSequence.replaceAll('-', '')
352
-
353
- const proteinView = getProteinViews(getSession(self).views).find(
354
- v => v.id === proteinViewId,
355
- )
356
- if (!proteinView) {
357
- throw new Error(`ProteinView "${proteinViewId}" not found`)
358
- }
359
-
360
- const structure = proteinView.structures[structureIdx]
361
- if (!structure) {
362
- throw new Error(`Structure at index ${structureIdx} not found`)
363
- }
364
-
365
- const structureSequence = structure.structureSequences?.[0]
366
- if (!structureSequence) {
367
- throw new Error('Structure sequence not available')
368
- }
369
-
370
- const alignment = runPairwiseAlignment(
371
- ungappedMsaSequence,
372
- structureSequence,
373
- )
374
- const { seq1ToSeq2 } = buildAlignmentMaps(alignment)
375
-
376
- const connection: StructureConnection = {
377
- proteinViewId,
378
- structureIdx,
379
- msaRowName: rowName,
380
- msaToStructure: Object.fromEntries(seq1ToSeq2),
381
- }
382
-
383
- self.connectedStructures.push(connection)
384
- },
385
-
386
- /**
387
- * #action
388
- */
389
- disconnectFromStructure(proteinViewId: string, structureIdx: number) {
390
- const idx = self.connectedStructures.findIndex(
391
- c =>
392
- c.proteinViewId === proteinViewId &&
393
- c.structureIdx === structureIdx,
394
- )
395
- if (idx !== -1) {
396
- self.connectedStructures.splice(idx, 1)
397
- }
398
- },
399
-
400
- /**
401
- * #action
402
- */
403
- disconnectAllStructures() {
404
- self.connectedStructures.clear()
405
- },
406
299
  }))
407
300
  .actions(self => {
408
301
  const superSetMouseClickPos = self.setMouseClickPos.bind(self)
@@ -434,28 +327,6 @@ export default function stateModelFactory() {
434
327
  self.setZoomToBaseLevel(!self.zoomToBaseLevel)
435
328
  },
436
329
  },
437
- {
438
- label: 'Connect to protein structure...',
439
- onClick: () => {
440
- getSession(self).queueDialog(handleClose => [
441
- ConnectStructureDialog,
442
- {
443
- model: self,
444
- handleClose,
445
- },
446
- ])
447
- },
448
- },
449
- ...(self.connectedStructures.length > 0
450
- ? [
451
- {
452
- label: 'Disconnect from protein structures',
453
- onClick: () => {
454
- self.disconnectAllStructures()
455
- },
456
- },
457
- ]
458
- : []),
459
330
  ]
460
331
  },
461
332
  }))
@@ -468,8 +339,6 @@ export default function stateModelFactory() {
468
339
  storeDataToIndexedDB,
469
340
  launchBlastIfNeeded,
470
341
  processInit,
471
- highlightConnectedStructures,
472
- autoConnectStructures,
473
342
  autoLoadProteinDomains,
474
343
  ]) {
475
344
  addDisposer(
@@ -1,58 +1,6 @@
1
1
  import { describe, expect, test } from 'vitest'
2
2
 
3
- import {
4
- gappedToUngappedPosition,
5
- structureMatchesMsa,
6
- } from './structureConnection'
7
-
8
- describe('structureMatchesMsa', () => {
9
- test('matches on a shared genome view alone (no UniProt id)', () => {
10
- expect(
11
- structureMatchesMsa({
12
- structure: { connectedViewId: 'lgv-TP53' },
13
- connectedViewId: 'lgv-TP53',
14
- }),
15
- ).toBe(true)
16
- })
17
-
18
- test('matches on a shared UniProt id alone (no genome view)', () => {
19
- expect(
20
- structureMatchesMsa({
21
- structure: { uniprotId: 'P04637' },
22
- uniprotId: 'P04637',
23
- }),
24
- ).toBe(true)
25
- })
26
-
27
- test('shared genome view wins even when UniProt ids differ', () => {
28
- expect(
29
- structureMatchesMsa({
30
- structure: { connectedViewId: 'lgv-TP53', uniprotId: 'OTHER' },
31
- connectedViewId: 'lgv-TP53',
32
- uniprotId: 'P04637',
33
- }),
34
- ).toBe(true)
35
- })
36
-
37
- test('no match when neither key matches', () => {
38
- expect(
39
- structureMatchesMsa({
40
- structure: { connectedViewId: 'lgv-OTHER', uniprotId: 'OTHER' },
41
- connectedViewId: 'lgv-TP53',
42
- uniprotId: 'P04637',
43
- }),
44
- ).toBe(false)
45
- })
46
-
47
- test('two undefined connectedViewIds do not count as a shared genome view', () => {
48
- // both sides lacking a genome view must NOT auto-pair on `undefined ===
49
- // undefined`; only an explicit shared id (or UniProt id) connects
50
- expect(structureMatchesMsa({ structure: {} })).toBe(false)
51
- expect(
52
- structureMatchesMsa({ structure: { uniprotId: 'P04637' } }),
53
- ).toBe(false)
54
- })
55
- })
3
+ import { gappedToUngappedPosition } from './structureConnection'
56
4
 
57
5
  describe('gappedToUngappedPosition', () => {
58
6
  test('returns correct ungapped position for non-gap character', () => {
@@ -4,9 +4,6 @@ export interface ProteinViewStructure {
4
4
  uniprotId?: string
5
5
  structureSequences?: string[]
6
6
  hoverGenomeHighlights?: { start: number; end: number }[]
7
- hoverPosition?: { structureSeqPos?: number }
8
- clearHighlightFromExternal?: () => void
9
- highlightFromExternal?: (pos: number) => void
10
7
  }
11
8
 
12
9
  export interface ProteinView {
@@ -28,50 +25,6 @@ export function getProteinViews(views: { type: string }[]): ProteinView[] {
28
25
  return (views as unknown[]).filter(isProteinView)
29
26
  }
30
27
 
31
- /**
32
- * Whether a 3D structure belongs to a given alignment — the single source of
33
- * truth for pairing an MsaView with a ProteinView's structure. A structure
34
- * matches when it either:
35
- * - shares the alignment's genome view (both pinned to the same
36
- * LinearGenomeView via `connectedViewId` — the genome-centric gene-explorer
37
- * flow, the same key genome↔MSA and genome↔structure already bridge through),
38
- * or
39
- * - shares the alignment's UniProt accession (the BLAST/AlphaFold flow, which
40
- * has no genome view to bridge through).
41
- *
42
- * The residue map itself is built by sequence (connectToStructure pairwise-
43
- * aligns the query row against the structure), so neither key is mechanically
44
- * required — they only scope WHICH structure pairs with the alignment.
45
- */
46
- export function structureMatchesMsa({
47
- structure,
48
- connectedViewId,
49
- uniprotId,
50
- }: {
51
- structure: Pick<ProteinViewStructure, 'connectedViewId' | 'uniprotId'>
52
- connectedViewId?: string
53
- uniprotId?: string
54
- }) {
55
- const sharesGenomeView =
56
- !!connectedViewId && structure.connectedViewId === connectedViewId
57
- const sharesUniprot = !!uniprotId && structure.uniprotId === uniprotId
58
- return sharesGenomeView || sharesUniprot
59
- }
60
-
61
- /**
62
- * Represents a connection between the MSA view and a protein structure
63
- */
64
- export interface StructureConnection {
65
- /** ID of the ProteinView containing the structure */
66
- proteinViewId: string
67
- /** Index of the structure within the ProteinView's structures array */
68
- structureIdx: number
69
- /** Name of the MSA row that corresponds to this structure */
70
- msaRowName: string
71
- /** Map from MSA ungapped position to structure sequence position */
72
- msaToStructure: Record<number, number>
73
- }
74
-
75
28
  /**
76
29
  * Helper to convert gapped MSA column to ungapped position for a specific row
77
30
  */
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.6.3'
1
+ export const version = '2.6.4'
@@ -1 +0,0 @@
1
- export {};
@@ -1,60 +0,0 @@
1
- import { getSession } from '@jbrowse/core/util';
2
- import { beforeEach, describe, expect, test, vi } from 'vitest';
3
- import { autoConnectStructures } from './afterCreateAutoruns';
4
- // Integration coverage for the autorun itself — the structure-matching matrix
5
- // lives in structureConnection.test.ts (structureMatchesMsa). Here we check the
6
- // autorun wires a match through to connectToStructure and respects its guards.
7
- // Mock only getSession; keep the rest of the util module real so the
8
- // afterCreateAutoruns import graph still loads.
9
- vi.mock('@jbrowse/core/util', async (importOriginal) => ({
10
- ...(await importOriginal()),
11
- getSession: vi.fn(),
12
- }));
13
- const mockGetSession = vi.mocked(getSession);
14
- function makeModel(opts) {
15
- const connected = [];
16
- const model = {
17
- connectedViewId: opts.connectedViewId,
18
- uniprotId: opts.uniprotId,
19
- rows: [['hg38', 'MKATEST']],
20
- connectedStructures: connected,
21
- connectToStructure: (proteinViewId, structureIdx) => {
22
- connected.push({ proteinViewId, structureIdx });
23
- },
24
- };
25
- return { model, connected };
26
- }
27
- function withStructure(structure) {
28
- const view = {
29
- type: 'ProteinView',
30
- id: 'pv1',
31
- structures: [{ ...structure, structureSequences: ['MKATEST'] }],
32
- };
33
- mockGetSession.mockReturnValue({
34
- views: [view],
35
- });
36
- }
37
- describe('autoConnectStructures', () => {
38
- beforeEach(() => {
39
- vi.clearAllMocks();
40
- });
41
- test('connects a matching structure (genome-view link, no UniProt id)', () => {
42
- const { model, connected } = makeModel({ connectedViewId: 'lgv-TP53' });
43
- withStructure({ connectedViewId: 'lgv-TP53' });
44
- autoConnectStructures(model);
45
- expect(connected).toEqual([{ proteinViewId: 'pv1', structureIdx: 0 }]);
46
- });
47
- test('does not connect a non-matching structure', () => {
48
- const { model, connected } = makeModel({ connectedViewId: 'lgv-TP53' });
49
- withStructure({ connectedViewId: 'lgv-OTHER' });
50
- autoConnectStructures(model);
51
- expect(connected).toEqual([]);
52
- });
53
- test('does not connect before the alignment has loaded (no rows)', () => {
54
- const { model, connected } = makeModel({ connectedViewId: 'lgv-TP53' });
55
- model.rows = [];
56
- withStructure({ connectedViewId: 'lgv-TP53' });
57
- autoConnectStructures(model);
58
- expect(connected).toEqual([]);
59
- });
60
- });
@@ -1,7 +0,0 @@
1
- import React from 'react';
2
- import type { JBrowsePluginMsaViewModel } from '../model';
3
- declare const ConnectStructureDialog: ({ model, handleClose, }: {
4
- model: JBrowsePluginMsaViewModel;
5
- handleClose: () => void;
6
- }) => React.JSX.Element;
7
- export default ConnectStructureDialog;
@@ -1,60 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { Dialog, ErrorMessage } from '@jbrowse/core/ui';
3
- import { getSession } from '@jbrowse/core/util';
4
- import { Button, DialogActions, DialogContent, FormControl, InputLabel, MenuItem, Select, Typography, } from '@mui/material';
5
- import { observer } from 'mobx-react';
6
- import { makeStyles } from 'tss-react/mui';
7
- import { getProteinViews } from '../structureConnection';
8
- const useStyles = makeStyles()(theme => ({
9
- formControl: {
10
- marginBottom: theme.spacing(2),
11
- },
12
- }));
13
- const ConnectStructureDialog = observer(function ConnectStructureDialog({ model, handleClose, }) {
14
- const { classes } = useStyles();
15
- const session = getSession(model);
16
- const [selectedViewId, setSelectedViewId] = useState('');
17
- const [selectedStructureIdx, setSelectedStructureIdx] = useState(0);
18
- const [selectedMsaRow, setSelectedMsaRow] = useState(model.querySeqName);
19
- const [error, setError] = useState();
20
- const proteinViews = getProteinViews(session.views);
21
- const selectedView = proteinViews.find(v => v.id === selectedViewId);
22
- const structures = selectedView?.structures ?? [];
23
- const msaRowNames = model.rows.map(r => r[0]);
24
- const handleConnect = () => {
25
- if (!selectedViewId) {
26
- setError('Please select a protein view');
27
- return;
28
- }
29
- try {
30
- model.connectToStructure(selectedViewId, selectedStructureIdx, selectedMsaRow);
31
- handleClose();
32
- }
33
- catch (e) {
34
- setError(e instanceof Error ? e.message : String(e));
35
- }
36
- };
37
- return (React.createElement(Dialog, { maxWidth: "sm", title: "Connect to Protein Structure", open: true, onClose: handleClose },
38
- React.createElement(DialogContent, null, proteinViews.length === 0 ? (React.createElement(Typography, { color: "textSecondary" }, "No protein views are currently open. Please open a protein structure view first.")) : (React.createElement(React.Fragment, null,
39
- React.createElement(FormControl, { fullWidth: true, className: classes.formControl },
40
- React.createElement(InputLabel, null, "Protein View"),
41
- React.createElement(Select, { value: selectedViewId, label: "Protein View", onChange: e => {
42
- setSelectedViewId(e.target.value);
43
- setSelectedStructureIdx(0);
44
- } }, proteinViews.map(view => (React.createElement(MenuItem, { key: view.id, value: view.id }, view.displayName ?? `ProteinView ${view.id}`))))),
45
- structures.length > 1 ? (React.createElement(FormControl, { fullWidth: true, className: classes.formControl },
46
- React.createElement(InputLabel, null, "Structure"),
47
- React.createElement(Select, { value: selectedStructureIdx, label: "Structure", onChange: e => {
48
- setSelectedStructureIdx(e.target.value);
49
- } }, structures.map((structure, idx) => (React.createElement(MenuItem, { key: idx, value: idx }, structure.url ?? `Structure ${idx + 1}`)))))) : null,
50
- React.createElement(FormControl, { fullWidth: true, className: classes.formControl },
51
- React.createElement(InputLabel, null, "MSA Row"),
52
- React.createElement(Select, { value: selectedMsaRow, label: "MSA Row", onChange: e => {
53
- setSelectedMsaRow(e.target.value);
54
- } }, msaRowNames.map(name => (React.createElement(MenuItem, { key: name, value: name }, name))))),
55
- error ? React.createElement(ErrorMessage, { error: error }) : null))),
56
- React.createElement(DialogActions, null,
57
- React.createElement(Button, { onClick: handleClose }, "Cancel"),
58
- React.createElement(Button, { onClick: handleConnect, variant: "contained", disabled: proteinViews.length === 0 || !selectedViewId }, "Connect"))));
59
- });
60
- export default ConnectStructureDialog;
@@ -1,20 +0,0 @@
1
- interface AlignmentResult {
2
- alignedSeq1: string;
3
- alignedSeq2: string;
4
- score: number;
5
- }
6
- export interface AlignmentRow {
7
- id: string;
8
- seq: string;
9
- }
10
- export interface PairwiseAlignment {
11
- consensus: string;
12
- alns: readonly [AlignmentRow, AlignmentRow];
13
- }
14
- export declare function needlemanWunsch(seq1: string, seq2: string, gapOpen?: number, gapExtend?: number): AlignmentResult;
15
- export declare function runPairwiseAlignment(seq1: string, seq2: string): PairwiseAlignment;
16
- export declare function buildAlignmentMaps(pairwiseAlignment: PairwiseAlignment): {
17
- seq1ToSeq2: Map<number, number>;
18
- seq2ToSeq1: Map<number, number>;
19
- };
20
- export {};