jbrowse-plugin-msaview 3.9.0 → 4.0.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 (156) hide show
  1. package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +1 -1
  2. package/dist/AddHighlightModel/index.js +1 -1
  3. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +2 -8
  4. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +10 -3
  5. package/dist/LaunchMsaView/cleanProteinSequence.test.js +36 -2
  6. package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +8 -10
  7. package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +9 -11
  8. package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.js +3 -1
  9. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +14 -16
  10. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +16 -18
  11. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +17 -19
  12. package/dist/LaunchMsaView/components/launchConnectedView.d.ts +2 -2
  13. package/dist/LaunchMsaView/components/launchConnectedView.js +4 -8
  14. package/dist/LaunchMsaView/components/useFeatureSequence.js +3 -6
  15. package/dist/LaunchMsaView/index.js +2 -1
  16. package/dist/LaunchMsaView/launchTarget.test.js +1 -1
  17. package/dist/LaunchMsaViewExtensionPoint/index.js +3 -3
  18. package/dist/LaunchMsaViewExtensionPoint/index.test.js +24 -21
  19. package/dist/MsaViewPanel/index.js +3 -3
  20. package/dist/MsaViewPanel/isMsaView.d.ts +4 -0
  21. package/dist/MsaViewPanel/isMsaView.js +3 -0
  22. package/dist/MsaViewPanel/model.d.ts +638 -212
  23. package/dist/MsaViewPanel/model.js +0 -3
  24. package/dist/MsaViewPanel/resolveConnectedTranscript.js +7 -13
  25. package/dist/MsaViewPanel/resolveConnectedTranscript.test.js +2 -2
  26. package/dist/MsaViewPanel/transcriptMap.js +27 -8
  27. package/dist/MsaViewPanel/transcriptMap.test.js +19 -0
  28. package/dist/chunks/AboutDialog-WT6GXMXU.js +2 -0
  29. package/dist/chunks/AboutDialog-WT6GXMXU.js.map +7 -0
  30. package/dist/chunks/AnnotationFileDialog-34R7LFHB.js +2 -0
  31. package/dist/chunks/AnnotationFileDialog-34R7LFHB.js.map +7 -0
  32. package/dist/chunks/BgzipFastaMsaAdapter-ZMUARSUQ.js +4 -0
  33. package/dist/chunks/BgzipFastaMsaAdapter-ZMUARSUQ.js.map +7 -0
  34. package/dist/chunks/ExportSVGDialog-NMVUHFGY.js +2 -0
  35. package/dist/chunks/ExportSVGDialog-NMVUHFGY.js.map +7 -0
  36. package/dist/chunks/FeatureDialog-XNHIA5PS.js +2 -0
  37. package/dist/chunks/FeatureDialog-XNHIA5PS.js.map +7 -0
  38. package/dist/chunks/HelpDialog-DF4Y536X.js +2 -0
  39. package/dist/chunks/HelpDialog-DF4Y536X.js.map +7 -0
  40. package/dist/chunks/LaunchMsaViewDialog-54LBEZHK.js +5 -0
  41. package/dist/chunks/LaunchMsaViewDialog-54LBEZHK.js.map +7 -0
  42. package/dist/chunks/MetadataDialog-2WT6EX4M.js +2 -0
  43. package/dist/chunks/MetadataDialog-2WT6EX4M.js.map +7 -0
  44. package/dist/chunks/MsaViewPanel-YYP4CYYO.js +2 -0
  45. package/dist/chunks/MsaViewPanel-YYP4CYYO.js.map +7 -0
  46. package/dist/chunks/TrackInfoDialog-TYCMLUKG.js +2 -0
  47. package/dist/chunks/TrackInfoDialog-TYCMLUKG.js.map +7 -0
  48. package/dist/chunks/TreeNodeInfoDialog-BFF6JRCO.js +2 -0
  49. package/dist/chunks/TreeNodeInfoDialog-BFF6JRCO.js.map +7 -0
  50. package/dist/chunks/chunk-2IWD4OO4.js +2 -0
  51. package/dist/chunks/chunk-2IWD4OO4.js.map +7 -0
  52. package/dist/chunks/chunk-5ORPTTMR.js +2 -0
  53. package/dist/chunks/chunk-5ORPTTMR.js.map +7 -0
  54. package/dist/chunks/chunk-6YFCSBEE.js +2 -0
  55. package/dist/chunks/chunk-6YFCSBEE.js.map +7 -0
  56. package/dist/chunks/chunk-73KAPCJ2.js +2 -0
  57. package/dist/chunks/chunk-73KAPCJ2.js.map +7 -0
  58. package/dist/chunks/chunk-BX4Z274Q.js +2 -0
  59. package/dist/chunks/chunk-BX4Z274Q.js.map +7 -0
  60. package/dist/chunks/chunk-CVLB4VFP.js +2 -0
  61. package/dist/chunks/chunk-CVLB4VFP.js.map +7 -0
  62. package/dist/chunks/chunk-DNFD5II2.js +2 -0
  63. package/dist/chunks/chunk-DNFD5II2.js.map +7 -0
  64. package/dist/chunks/chunk-ENTWPX2K.js +6 -0
  65. package/dist/chunks/chunk-ENTWPX2K.js.map +7 -0
  66. package/dist/chunks/chunk-EO7URJUB.js +2 -0
  67. package/dist/chunks/chunk-EO7URJUB.js.map +7 -0
  68. package/dist/chunks/chunk-FCNT6B47.js +2 -0
  69. package/dist/chunks/chunk-FCNT6B47.js.map +7 -0
  70. package/dist/chunks/chunk-FCWXYIFC.js +2 -0
  71. package/dist/chunks/chunk-FCWXYIFC.js.map +7 -0
  72. package/dist/chunks/chunk-FY3BTKY4.js +2 -0
  73. package/dist/chunks/chunk-FY3BTKY4.js.map +7 -0
  74. package/dist/chunks/chunk-IBBQUV2I.js +2 -0
  75. package/dist/chunks/chunk-IBBQUV2I.js.map +7 -0
  76. package/dist/chunks/chunk-J6KYLXRD.js +2 -0
  77. package/dist/chunks/chunk-J6KYLXRD.js.map +7 -0
  78. package/dist/chunks/chunk-MBI4TMKR.js +2 -0
  79. package/dist/chunks/chunk-MBI4TMKR.js.map +7 -0
  80. package/dist/chunks/chunk-NSASZ5FM.js +2 -0
  81. package/dist/chunks/chunk-NSASZ5FM.js.map +7 -0
  82. package/dist/chunks/chunk-O7JNDIKD.js +2 -0
  83. package/dist/chunks/chunk-O7JNDIKD.js.map +7 -0
  84. package/dist/chunks/chunk-P77DPK6B.js +2 -0
  85. package/dist/chunks/chunk-P77DPK6B.js.map +7 -0
  86. package/dist/chunks/chunk-PJZ3XTMG.js +6 -0
  87. package/dist/chunks/chunk-PJZ3XTMG.js.map +7 -0
  88. package/dist/chunks/chunk-QAC6JSDZ.js +5 -0
  89. package/dist/chunks/chunk-QAC6JSDZ.js.map +7 -0
  90. package/dist/chunks/chunk-QMLPITS4.js +18 -0
  91. package/dist/chunks/chunk-QMLPITS4.js.map +7 -0
  92. package/dist/chunks/chunk-RKVHQK52.js +3 -0
  93. package/dist/chunks/chunk-RKVHQK52.js.map +7 -0
  94. package/dist/chunks/chunk-RTKNCGDF.js +8 -0
  95. package/dist/chunks/chunk-RTKNCGDF.js.map +7 -0
  96. package/dist/chunks/chunk-TITP6LCV.js +2 -0
  97. package/dist/chunks/chunk-TITP6LCV.js.map +7 -0
  98. package/dist/chunks/chunk-YUKJI2AH.js +2 -0
  99. package/dist/chunks/chunk-YUKJI2AH.js.map +7 -0
  100. package/dist/chunks/chunk-ZTQYZDWK.js +2 -0
  101. package/dist/chunks/chunk-ZTQYZDWK.js.map +7 -0
  102. package/dist/chunks/dist-2T65ZS4E.js +2 -0
  103. package/dist/chunks/dist-2T65ZS4E.js.map +7 -0
  104. package/dist/chunks/dist-UX5CKLQJ.js +2 -0
  105. package/dist/chunks/dist-UX5CKLQJ.js.map +7 -0
  106. package/dist/chunks/model-KEZXRCK6.js +12 -0
  107. package/dist/chunks/model-KEZXRCK6.js.map +7 -0
  108. package/dist/chunks/renderToSvg-B7RCHRO3.js +2 -0
  109. package/dist/chunks/renderToSvg-B7RCHRO3.js.map +7 -0
  110. package/dist/config.json +1 -1
  111. package/dist/index.d.ts +11 -8
  112. package/dist/index.js +4 -1
  113. package/dist/jbrowse-plugin-msaview.esm.js +3 -0
  114. package/dist/jbrowse-plugin-msaview.esm.js.map +7 -0
  115. package/dist/utils/launchMsaView.d.ts +1 -1
  116. package/dist/utils/launchMsaView.js +5 -2
  117. package/dist/utils/workspaces.test.js +5 -5
  118. package/dist/version.d.ts +1 -1
  119. package/dist/version.js +1 -1
  120. package/package.json +19 -20
  121. package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +1 -1
  122. package/src/AddHighlightModel/index.tsx +1 -1
  123. package/src/LaunchMsaView/cleanProteinSequence.test.ts +37 -2
  124. package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +3 -3
  125. package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +3 -3
  126. package/src/LaunchMsaView/components/BlastQuery/CachedBlastResults.tsx +3 -1
  127. package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +3 -3
  128. package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +3 -3
  129. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +3 -3
  130. package/src/LaunchMsaView/components/launchConnectedView.ts +5 -8
  131. package/src/LaunchMsaView/components/useFeatureSequence.ts +3 -6
  132. package/src/LaunchMsaView/index.ts +6 -1
  133. package/src/LaunchMsaView/launchTarget.test.ts +1 -1
  134. package/src/LaunchMsaView/launchTarget.ts +2 -3
  135. package/src/LaunchMsaViewExtensionPoint/index.test.ts +26 -21
  136. package/src/LaunchMsaViewExtensionPoint/index.ts +3 -4
  137. package/src/MsaViewPanel/index.ts +3 -4
  138. package/src/MsaViewPanel/isMsaView.ts +7 -0
  139. package/src/MsaViewPanel/model.ts +0 -6
  140. package/src/MsaViewPanel/resolveConnectedTranscript.test.ts +2 -2
  141. package/src/MsaViewPanel/resolveConnectedTranscript.ts +7 -17
  142. package/src/MsaViewPanel/transcriptMap.test.ts +23 -0
  143. package/src/MsaViewPanel/transcriptMap.ts +36 -9
  144. package/src/index.ts +4 -1
  145. package/src/utils/launchMsaView.ts +5 -2
  146. package/src/utils/useFetch.ts +1 -1
  147. package/src/utils/workspaces.test.ts +5 -5
  148. package/src/version.ts +1 -1
  149. package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +0 -21
  150. package/dist/LaunchMsaView/components/calculateProteinSequence.js +0 -71
  151. package/dist/LaunchMsaView/components/types.d.ts +0 -6
  152. package/dist/LaunchMsaView/components/types.js +0 -1
  153. package/dist/jbrowse-plugin-msaview.umd.production.min.js +0 -143
  154. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +0 -7
  155. package/src/LaunchMsaView/components/calculateProteinSequence.ts +0 -113
  156. package/src/LaunchMsaView/components/types.ts +0 -8
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { getSession } from '@jbrowse/core/util';
3
3
  import { observer } from 'mobx-react';
4
- import { isMsaView } from '../MsaViewPanel/model';
4
+ import { isMsaView } from '../MsaViewPanel/isMsaView';
5
5
  import { hasHoverPosition, useStyles } from './util';
6
6
  const GenomeMouseoverHighlight = observer(function ({ model, }) {
7
7
  const { hovered, views } = getSession(model);
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { getSession } from '@jbrowse/core/util';
3
- import { isMsaView } from '../MsaViewPanel/model';
3
+ import { isMsaView } from '../MsaViewPanel/isMsaView';
4
4
  import HighlightComponents from './HighlightComponents';
5
5
  export default function AddHighlightComponentsModelF(pluginManager) {
6
6
  pluginManager.addToExtensionPoint(
@@ -3,14 +3,8 @@ import type { BaseFeatureDataAdapter } from '@jbrowse/core/data_adapters/BaseAda
3
3
  export default class BgzipFastaMsaAdapter extends BaseAdapter {
4
4
  configureP: Promise<BaseFeatureDataAdapter> | undefined;
5
5
  refNamesP: Promise<string[]> | undefined;
6
- configurePre(): Promise<BaseFeatureDataAdapter<import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
7
- setSubschema(slotName: string, data: Record<string, unknown>): any;
8
- setSlot(slotName: string, value: unknown): void;
9
- } & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>>;
10
- configure(): Promise<BaseFeatureDataAdapter<import("@jbrowse/mobx-state-tree").ModelInstanceTypeProps<Record<string, any>> & {
11
- setSubschema(slotName: string, data: Record<string, unknown>): any;
12
- setSlot(slotName: string, value: unknown): void;
13
- } & import("@jbrowse/mobx-state-tree").IStateTreeNode<import("@jbrowse/core/configuration").AnyConfigurationSchemaType>>>;
6
+ configurePre(): Promise<BaseFeatureDataAdapter<import("@jbrowse/core/configuration").AnyConfigurationModel>>;
7
+ configure(): Promise<BaseFeatureDataAdapter<import("@jbrowse/core/configuration").AnyConfigurationModel>>;
14
8
  getMSARefs(): Promise<string[]>;
15
9
  getMsaRegex(): RegExp;
16
10
  refNameToMsaId(refName: string): string;
@@ -1,7 +1,14 @@
1
1
  import type PluginManager from '@jbrowse/core/PluginManager';
2
2
  export default function configSchemaF(pluginManager: PluginManager): import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaType<{
3
+ msaRegex: {
4
+ type: "string";
5
+ } & {
6
+ defaultValue: string;
7
+ };
8
+ } | {
3
9
  readonly msaRegex: {
4
- readonly type: "string";
5
- readonly defaultValue: "_";
10
+ type: "string";
11
+ } & {
12
+ defaultValue: string;
6
13
  };
7
- }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<import("@jbrowse/core/configuration").AnyConfigurationSchemaType, undefined>>;
14
+ }, import("@jbrowse/core/configuration/configurationSchema").ConfigurationSchemaOptions<any, undefined, undefined, import("@jbrowse/core/configuration").ConfigurationSchemaRequirement<Partial<Record<string, string[]>>, string>>>;
@@ -1,8 +1,8 @@
1
1
  import { SimpleFeature } from '@jbrowse/core/util';
2
+ import { translateTranscript } from '@jbrowse/core/util/translateTranscript';
2
3
  import { genomeToTranscriptSeqMapping } from 'g2p_mapper';
3
4
  import { describe, expect, test } from 'vitest';
4
5
  import { transcriptPosToVisibleCol } from '../MsaViewPanel/util';
5
- import { getProteinSequenceFromFeature } from './components/calculateProteinSequence';
6
6
  import { findQueryRow } from './detectQueryRow';
7
7
  import { cleanProteinSequence } from './util';
8
8
  describe('cleanProteinSequence', () => {
@@ -22,7 +22,7 @@ describe('a launched query row read through g2p', () => {
22
22
  function rowResidueUnder(genomePos, json) {
23
23
  const seq = json.seq;
24
24
  const feature = new SimpleFeature(json);
25
- const row = cleanProteinSequence(getProteinSequenceFromFeature({ seq, feature }));
25
+ const row = cleanProteinSequence(translateTranscript({ transcript: feature, seq })?.protein ?? '');
26
26
  const { g2p } = genomeToTranscriptSeqMapping(feature.toJSON());
27
27
  const col = transcriptPosToVisibleCol({
28
28
  querySeqName: 'QUERY',
@@ -52,6 +52,40 @@ describe('a launched query row read through g2p', () => {
52
52
  expect(rowResidueUnder(4, json)).toBe('A');
53
53
  expect(rowResidueUnder(10, json)).toBe('K');
54
54
  });
55
+ test('on the reverse strand, across an intron, after a partial first codon', () => {
56
+ // sense G | ATG GCC <intron> TGG AAA TAA, stored as the reverse complement
57
+ const json = {
58
+ uniqueId: 't1',
59
+ refName: 'chr1',
60
+ start: 0,
61
+ end: 24,
62
+ strand: -1,
63
+ type: 'mRNA',
64
+ seq: 'TTATTTCCACTACTTACGGCCATC',
65
+ subfeatures: [
66
+ {
67
+ uniqueId: 'c1',
68
+ refName: 'chr1',
69
+ type: 'CDS',
70
+ start: 17,
71
+ end: 24,
72
+ phase: 1,
73
+ },
74
+ {
75
+ uniqueId: 'c2',
76
+ refName: 'chr1',
77
+ type: 'CDS',
78
+ start: 0,
79
+ end: 9,
80
+ phase: 0,
81
+ },
82
+ ],
83
+ };
84
+ expect(rowResidueUnder(22, json)).toBe('M');
85
+ expect(rowResidueUnder(18, json)).toBe('A');
86
+ expect(rowResidueUnder(7, json)).toBe('W');
87
+ expect(rowResidueUnder(4, json)).toBe('K');
88
+ });
55
89
  test('after an internal stop', () => {
56
90
  // ATG TGA GCC TGG TAA: a selenocysteine GENCODE does not annotate
57
91
  const json = transcript('ATGTGAGCCTGGTAA', { type: 'CDS' });
@@ -103,16 +103,14 @@ const BlastAutomaticPanel = observer(function ({ handleClose, feature, model, ch
103
103
  selectedTranscript: selectedTranscript.toJSON(),
104
104
  proteinSequence,
105
105
  };
106
- submit(() => {
107
- launchConnectedView({
108
- view,
109
- feature: selectedTranscript,
110
- placement,
111
- displayName: getBlastViewTitle(feature, selectedTranscript),
112
- ...builtAlignmentLook,
113
- blastParams,
114
- });
115
- });
106
+ submit(() => launchConnectedView({
107
+ view,
108
+ feature: selectedTranscript,
109
+ placement,
110
+ displayName: getBlastViewTitle(feature, selectedTranscript),
111
+ ...builtAlignmentLook,
112
+ blastParams,
113
+ }));
116
114
  }
117
115
  }, onCancel: handleClose })));
118
116
  });
@@ -85,17 +85,15 @@ const BlastManualPanel = observer(function ({ handleClose, feature, model, child
85
85
  React.createElement(QueryRowSelector, { ...queryRow })))),
86
86
  React.createElement(SubmitCancelActions, { model: model, submitDisabled: !selectedTranscript || !msaText.trim(), onSubmit: placement => {
87
87
  if (selectedTranscript) {
88
- submit(() => {
89
- launchConnectedView({
90
- view,
91
- feature: selectedTranscript,
92
- placement,
93
- displayName: getGeneDisplayName(selectedTranscript),
94
- querySeqName: queryRow.querySeqName,
95
- querySeqOffset: queryRow.querySeqOffset,
96
- data: { msa: msaText, tree: treeText },
97
- });
98
- });
88
+ submit(() => launchConnectedView({
89
+ view,
90
+ feature: selectedTranscript,
91
+ placement,
92
+ displayName: getGeneDisplayName(selectedTranscript),
93
+ querySeqName: queryRow.querySeqName,
94
+ querySeqOffset: queryRow.querySeqOffset,
95
+ data: { msa: msaText, tree: treeText },
96
+ }));
99
97
  }
100
98
  }, onCancel: handleClose })));
101
99
  });
@@ -70,8 +70,10 @@ const CachedBlastResults = observer(function ({ model, handleClose, feature, })
70
70
  displayName: `BLAST - ${getResultDisplayName(cached)}`,
71
71
  ...builtAlignmentLook,
72
72
  data: { msa, tree, treeMetadata },
73
+ }).then(handleClose, (e) => {
74
+ console.error(e);
75
+ setOperationError(e);
73
76
  });
74
- handleClose();
75
77
  };
76
78
  const displayError = error ?? operationError;
77
79
  return displayError ? (React.createElement(ErrorMessage, { error: displayError })) : isLoading ? (React.createElement(Typography, null, "Loading cached results...")) : results.length === 0 ? (React.createElement(Typography, { color: "textSecondary" }, "No cached BLAST results found for this gene. Run a BLAST query to cache results.")) : (React.createElement("div", null,
@@ -80,22 +80,20 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
80
80
  (inputMethod === 'file' && !msaFileLocation) ||
81
81
  (inputMethod === 'text' && !msaText.trim()), onSubmit: placement => {
82
82
  if (selectedTranscript) {
83
- submit(() => {
84
- launchConnectedView({
85
- view,
86
- feature: selectedTranscript,
87
- placement,
88
- displayName: getGeneDisplayName(selectedTranscript),
89
- querySeqName: queryRow.querySeqName,
90
- querySeqOffset: queryRow.querySeqOffset,
91
- ...(inputMethod === 'file'
92
- ? {
93
- msaFilehandle: msaFileLocation,
94
- treeFilehandle: treeFileLocation,
95
- }
96
- : { data: { msa: msaText, tree: treeText } }),
97
- });
98
- });
83
+ submit(() => launchConnectedView({
84
+ view,
85
+ feature: selectedTranscript,
86
+ placement,
87
+ displayName: getGeneDisplayName(selectedTranscript),
88
+ querySeqName: queryRow.querySeqName,
89
+ querySeqOffset: queryRow.querySeqOffset,
90
+ ...(inputMethod === 'file'
91
+ ? {
92
+ msaFilehandle: msaFileLocation,
93
+ treeFilehandle: treeFileLocation,
94
+ }
95
+ : { data: { msa: msaText, tree: treeText } }),
96
+ }));
99
97
  }
100
98
  }, onCancel: handleClose })));
101
99
  });
@@ -60,24 +60,22 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, preferre
60
60
  React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection })),
61
61
  React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !rowCountValid, onSubmit: placement => {
62
62
  if (selectedTranscript) {
63
- submit(() => {
64
- launchConnectedView({
65
- view,
66
- feature: selectedTranscript,
67
- placement,
68
- displayName: `Orthologs - ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
69
- ...builtAlignmentLook,
70
- orthologParams: {
71
- taxId,
72
- source,
73
- maxSpecies: rowCount,
74
- geneCandidates,
75
- msaAlgorithm,
76
- selectedTranscript: selectedTranscript.toJSON(),
77
- proteinSequence,
78
- },
79
- });
80
- });
63
+ submit(() => launchConnectedView({
64
+ view,
65
+ feature: selectedTranscript,
66
+ placement,
67
+ displayName: `Orthologs - ${getGeneDisplayName(feature)} - ${getTranscriptDisplayName(selectedTranscript)}`,
68
+ ...builtAlignmentLook,
69
+ orthologParams: {
70
+ taxId,
71
+ source,
72
+ maxSpecies: rowCount,
73
+ geneCandidates,
74
+ msaAlgorithm,
75
+ selectedTranscript: selectedTranscript.toJSON(),
76
+ proteinSequence,
77
+ },
78
+ }));
81
79
  }
82
80
  }, onCancel: handleClose })));
83
81
  });
@@ -82,25 +82,23 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferred
82
82
  // dataset that does not cover this gene
83
83
  submitDisabled: !selectedTranscript || !msaData?.length || !querySeqName, onSubmit: placement => {
84
84
  if (selectedTranscript && msaText) {
85
- submit(() => {
86
- launchConnectedView({
87
- view,
88
- feature: selectedTranscript,
89
- placement,
90
- displayName: getGeneDisplayName(selectedTranscript),
91
- treeAreaWidth: 200,
92
- treeWidth: 100,
93
- drawNodeBubbles: false,
94
- labelsAlignRight: true,
95
- showBranchLen: false,
96
- colWidth: 10,
97
- rowHeight: 12,
98
- colorSchemeName: 'percent_identity_dynamic',
99
- querySeqName,
100
- querySeqOffset: queryRow.querySeqOffset,
101
- data: { msa: msaText },
102
- });
103
- });
85
+ submit(() => launchConnectedView({
86
+ view,
87
+ feature: selectedTranscript,
88
+ placement,
89
+ displayName: getGeneDisplayName(selectedTranscript),
90
+ treeAreaWidth: 200,
91
+ treeWidth: 100,
92
+ drawNodeBubbles: false,
93
+ labelsAlignRight: true,
94
+ showBranchLen: false,
95
+ colWidth: 10,
96
+ rowHeight: 12,
97
+ colorSchemeName: 'percent_identity_dynamic',
98
+ querySeqName,
99
+ querySeqOffset: queryRow.querySeqOffset,
100
+ data: { msa: msaText },
101
+ }));
104
102
  }
105
103
  }, onCancel: handleClose })));
106
104
  });
@@ -15,9 +15,9 @@ export declare function launchConnectedView({ view, feature, placement, ...snaps
15
15
  view: LinearGenomeViewModel;
16
16
  feature?: Feature;
17
17
  placement: MsaViewPlacement;
18
- } & Record<string, unknown>): void;
18
+ } & Record<string, unknown>): Promise<import("@jbrowse/core/util").AbstractViewModel>;
19
19
  /** runs a panel's launch, closing the dialog on success and keeping the error */
20
20
  export declare function useLaunchSubmit(handleClose: () => void): {
21
21
  launchError: unknown;
22
- submit: (launch: () => void) => void;
22
+ submit: (launch: () => Promise<unknown>) => void;
23
23
  };
@@ -12,7 +12,7 @@ export const builtAlignmentLook = {
12
12
  * through `feature` to the transcript whose codons its query row is read by.
13
13
  */
14
14
  export function launchConnectedView({ view, feature, placement, ...snapshot }) {
15
- launchMsaView(getSession(view), {
15
+ return launchMsaView(getSession(view), {
16
16
  placement,
17
17
  connectedViewId: view.id,
18
18
  connectedFeature: feature?.toJSON(),
@@ -25,15 +25,11 @@ export function useLaunchSubmit(handleClose) {
25
25
  return {
26
26
  launchError,
27
27
  submit: (launch) => {
28
- try {
29
- setLaunchError(undefined);
30
- launch();
31
- handleClose();
32
- }
33
- catch (e) {
28
+ setLaunchError(undefined);
29
+ launch().then(handleClose, (e) => {
34
30
  console.error(e);
35
31
  setLaunchError(e);
36
- }
32
+ });
37
33
  },
38
34
  };
39
35
  }
@@ -1,6 +1,6 @@
1
1
  import { getSession } from '@jbrowse/core/util';
2
+ import { translateTranscript } from '@jbrowse/core/util/translateTranscript';
2
3
  import { useFetch } from '../../utils/useFetch';
3
- import { getProteinSequenceFromFeature } from './calculateProteinSequence';
4
4
  import { fetchSeq } from './fetchSeq';
5
5
  export function useFeatureSequence({ view, feature, }) {
6
6
  const assemblyName = view?.assemblyNames?.[0];
@@ -18,11 +18,8 @@ export function useFeatureSequence({ view, feature, }) {
18
18
  });
19
19
  return {
20
20
  proteinSequence: sequence && feature
21
- ? getProteinSequenceFromFeature({
22
- seq: sequence.seq,
23
- feature,
24
- assemblyGeneticCodeId: sequence.assemblyGeneticCodeId,
25
- })
21
+ ? (translateTranscript({ transcript: feature, ...sequence })?.protein ??
22
+ '')
26
23
  : '',
27
24
  sequence,
28
25
  error,
@@ -1,8 +1,9 @@
1
+ import { lazy } from 'react';
1
2
  import { getContainingTrack, getSession } from '@jbrowse/core/util';
2
3
  import AddIcon from '@mui/icons-material/Add';
3
4
  import { isCodingFeature } from './codingFeature';
4
- import LaunchMsaViewDialog from './components/LaunchMsaViewDialog';
5
5
  import { launchTarget } from './launchTarget';
6
+ const LaunchMsaViewDialog = lazy(() => import('./components/LaunchMsaViewDialog'));
6
7
  function isDisplay(elt) {
7
8
  return elt.name === 'LinearBasicDisplay';
8
9
  }
@@ -52,7 +52,7 @@ describe('launchTarget', () => {
52
52
  test('offers nothing when nothing was clicked', () => {
53
53
  expect(launchTarget({ contextMenuItems: () => [] })).toBeUndefined();
54
54
  });
55
- // v3.7.0 hosts have contextMenuFeature and nothing else; dropping this
55
+ // v4.3.0 and earlier have contextMenuFeature and nothing else; dropping this
56
56
  // fallback once took "Launch MSA view" off every host in the wild.
57
57
  test('falls back to a synchronous contextMenuFeature', () => {
58
58
  const target = launchTarget(legacyHost(feature('mRNA')));
@@ -1,4 +1,3 @@
1
- import { expandSpec } from 'react-msaview';
2
1
  import { launchMsaView } from '../utils/launchMsaView';
3
2
  /**
4
3
  * `msa` and `tree` onto the plugin's own sources, so a url still goes through
@@ -23,7 +22,7 @@ function withLongSources({ msa, tree, ...args }) {
23
22
  };
24
23
  }
25
24
  export default function LaunchMsaViewExtensionPointF(pluginManager) {
26
- pluginManager.addToExtensionPoint('LaunchView-MsaView', (args) => {
25
+ pluginManager.addToExtensionPoint('LaunchView-MsaView', async (args) => {
27
26
  const { session, query, data, msaFileLocation, msaIndexedLocation, msaName, treeFileLocation, querySeqName, searchParams, ...rest } = withLongSources(args);
28
27
  // `orthologParams` and `searchParams` name no alignment at all — the
29
28
  // view builds one at launch, which is the dialog's Orthologs and BLAST
@@ -49,7 +48,8 @@ export default function LaunchMsaViewExtensionPointF(pluginManager) {
49
48
  msaName,
50
49
  querySeqName,
51
50
  };
52
- launchMsaView(session, {
51
+ const { expandSpec } = await import('react-msaview');
52
+ await launchMsaView(session, {
53
53
  ...expandSpec({ ...rest, query }),
54
54
  ...(searchParams ? { blastParams: searchParams } : {}),
55
55
  data,
@@ -1,6 +1,6 @@
1
1
  import { expect, test } from 'vitest';
2
2
  import LaunchMsaViewExtensionPointF from './index';
3
- function launch(args) {
3
+ async function launch(args) {
4
4
  let run;
5
5
  LaunchMsaViewExtensionPointF({
6
6
  addToExtensionPoint(_name, cb) {
@@ -9,20 +9,20 @@ function launch(args) {
9
9
  });
10
10
  const added = [];
11
11
  const session = {
12
- addView(_type, snapshot) {
12
+ async launchView(_type, snapshot) {
13
13
  added.push(snapshot);
14
14
  return { id: 'view-1' };
15
15
  },
16
16
  };
17
- run({ session, ...args });
17
+ await run({ session, ...args });
18
18
  return added[0];
19
19
  }
20
- test('inline data carries no init, which the panel would read as a launch in flight', () => {
21
- const snapshot = launch({ data: { msa: '>a\nMEEP' } });
20
+ test('inline data carries no init, which the panel would read as a launch in flight', async () => {
21
+ const snapshot = await launch({ data: { msa: '>a\nMEEP' } });
22
22
  expect('init' in snapshot).toBe(false);
23
23
  });
24
- test('a file location travels through init', () => {
25
- const snapshot = launch({
24
+ test('a file location travels through init', async () => {
25
+ const snapshot = await launch({
26
26
  msaFileLocation: { uri: 'http://example.com/a.fa' },
27
27
  });
28
28
  expect(snapshot.init).toEqual({
@@ -32,8 +32,8 @@ test('a file location travels through init', () => {
32
32
  querySeqName: undefined,
33
33
  });
34
34
  });
35
- test('searchParams is a source, stored under the name the dialog uses', () => {
36
- const snapshot = launch({
35
+ test('searchParams is a source, stored under the name the dialog uses', async () => {
36
+ const snapshot = await launch({
37
37
  searchParams: {
38
38
  searchProgram: 'phmmer',
39
39
  blastDatabase: 'rp15',
@@ -50,13 +50,13 @@ test('searchParams is a source, stored under the name the dialog uses', () => {
50
50
  expect('searchParams' in snapshot).toBe(false);
51
51
  expect(snapshot.connectedTranscript).toBe('NM_000546.6');
52
52
  });
53
- test('a launch naming no source at all is refused', () => {
54
- expect(() => launch({ connectedViewId: 'lgv1' })).toThrow(/searchParams/);
53
+ test('a launch naming no source at all is refused', async () => {
54
+ await expect(launch({ connectedViewId: 'lgv1' })).rejects.toThrow(/searchParams/);
55
55
  });
56
- test('region and the data-layer keys reach the view as snapshot properties', () => {
56
+ test('region and the data-layer keys reach the view as snapshot properties', async () => {
57
57
  const region = { row: 'Human', start: 245, end: 249 };
58
58
  const clades = [{ mrca: ['Human', 'Mouse'], tips: 2, mark: 'bracket' }];
59
- const snapshot = launch({
59
+ const snapshot = await launch({
60
60
  data: { msa: '>Human\nMEEP' },
61
61
  region,
62
62
  clades,
@@ -68,8 +68,11 @@ test('region and the data-layer keys reach the view as snapshot properties', ()
68
68
  expect(snapshot.relativeTo).toBe('Human');
69
69
  expect(snapshot.gffFilehandle).toEqual({ uri: 'http://example.com/a.gff' });
70
70
  });
71
- test('one field set is enough to need init', () => {
72
- const snapshot = launch({ data: { msa: '>a\nMEEP' }, querySeqName: 'QUERY' });
71
+ test('one field set is enough to need init', async () => {
72
+ const snapshot = await launch({
73
+ data: { msa: '>a\nMEEP' },
74
+ querySeqName: 'QUERY',
75
+ });
73
76
  expect(snapshot.init).toEqual({
74
77
  msaUrl: undefined,
75
78
  msaIndexedLocation: undefined,
@@ -77,8 +80,8 @@ test('one field set is enough to need init', () => {
77
80
  querySeqName: 'QUERY',
78
81
  });
79
82
  });
80
- test('msa and tree urls reach the plugin sources, query the query row', () => {
81
- const snapshot = launch({
83
+ test('msa and tree urls reach the plugin sources, query the query row', async () => {
84
+ const snapshot = await launch({
82
85
  msa: 'https://example.com/p53.afa',
83
86
  tree: 'https://example.com/p53.nh',
84
87
  query: 'Human',
@@ -94,13 +97,13 @@ test('msa and tree urls reach the plugin sources, query the query row', () => {
94
97
  expect(snapshot.relativeTo).toBe('Human');
95
98
  expect('msa' in snapshot || 'query' in snapshot).toBe(false);
96
99
  });
97
- test('inline msa and newick text become inline data', () => {
98
- const snapshot = launch({ msa: '>a\nMEEP\n>b\nMEEP', tree: '(a,b);' });
100
+ test('inline msa and newick text become inline data', async () => {
101
+ const snapshot = await launch({ msa: '>a\nMEEP\n>b\nMEEP', tree: '(a,b);' });
99
102
  expect(snapshot.data).toEqual({ msa: '>a\nMEEP\n>b\nMEEP', tree: '(a,b);' });
100
103
  expect('init' in snapshot).toBe(false);
101
104
  });
102
- test('highlights, region and column tracks expand onto the query row', () => {
103
- const snapshot = launch({
105
+ test('highlights, region and column tracks expand onto the query row', async () => {
106
+ const snapshot = await launch({
104
107
  msa: 'https://example.com/p53.afa',
105
108
  query: 'Human',
106
109
  highlights: ['102-292 DNA-binding', 175],
@@ -1,13 +1,13 @@
1
1
  import { lazy } from 'react';
2
2
  import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType';
3
- import stateModelFactory from './model';
4
- // lazies
5
3
  const MsaViewPanel = lazy(() => import('./components/MsaViewPanel'));
6
4
  export default function MsaViewF(pluginManager) {
7
5
  pluginManager.addViewType(() => {
8
6
  return new ViewType({
9
7
  name: 'MsaView',
10
- stateModel: stateModelFactory(),
8
+ // a thunk, so react-msaview loads with the first view rather than with
9
+ // the plugin; addView refuses the type until then, hence launchView
10
+ stateModel: () => import('./model').then(m => m.default()),
11
11
  ReactComponent: MsaViewPanel,
12
12
  });
13
13
  });
@@ -0,0 +1,4 @@
1
+ import type { JBrowsePluginMsaViewModel } from './model';
2
+ export declare function isMsaView(view: {
3
+ type: string;
4
+ }): view is JBrowsePluginMsaViewModel;
@@ -0,0 +1,3 @@
1
+ export function isMsaView(view) {
2
+ return view.type === 'MsaView';
3
+ }