@apollo-annotation/jbrowse-plugin-apollo 1.0.0 → 1.1.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 (172) hide show
  1. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts +8 -2
  2. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts.map +1 -1
  3. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts +8 -3
  4. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts.map +1 -1
  5. package/dist/ApolloInternetAccount/configSchema.d.ts +44 -0
  6. package/dist/ApolloInternetAccount/configSchema.d.ts.map +1 -0
  7. package/dist/ApolloInternetAccount/model.d.ts +174 -0
  8. package/dist/ApolloInternetAccount/model.d.ts.map +1 -0
  9. package/dist/ApolloJobModel.d.ts.map +1 -1
  10. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts +8 -0
  11. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts.map +1 -0
  12. package/dist/ApolloSequenceAdapter/configSchema.d.ts +8 -0
  13. package/dist/ApolloSequenceAdapter/configSchema.d.ts.map +1 -0
  14. package/dist/ApolloTextSearchAdapter/configSchema.d.ts +20 -0
  15. package/dist/ApolloTextSearchAdapter/configSchema.d.ts.map +1 -0
  16. package/dist/BackendDrivers/CollaborationServerDriver.d.ts +1 -1
  17. package/dist/BackendDrivers/CollaborationServerDriver.d.ts.map +1 -1
  18. package/dist/BackendDrivers/LocalDriver/LocalDriver.d.ts.map +1 -1
  19. package/dist/FeatureDetailsWidget/ApolloFeatureDetailsWidget.d.ts.map +1 -1
  20. package/dist/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.d.ts.map +1 -1
  21. package/dist/FeatureDetailsWidget/Attributes.d.ts.map +1 -1
  22. package/dist/FeatureDetailsWidget/DefaultAttributeEditor.d.ts.map +1 -1
  23. package/dist/FeatureDetailsWidget/DefaultAttributeViewer.d.ts.map +1 -1
  24. package/dist/FeatureDetailsWidget/NumberTextField.d.ts.map +1 -1
  25. package/dist/FeatureDetailsWidget/StringTextField.d.ts.map +1 -1
  26. package/dist/FeatureDetailsWidget/TranscriptSequence.d.ts.map +1 -1
  27. package/dist/FeatureDetailsWidget/TranscriptWidgetEditLocation.d.ts.map +1 -1
  28. package/dist/FeatureDetailsWidget/Translation.d.ts +14 -0
  29. package/dist/FeatureDetailsWidget/Translation.d.ts.map +1 -0
  30. package/dist/LinearApolloDisplay/components/CheckResultWarnings.d.ts.map +1 -1
  31. package/dist/LinearApolloDisplay/configSchema.d.ts +2 -0
  32. package/dist/LinearApolloDisplay/configSchema.d.ts.map +1 -0
  33. package/dist/LinearApolloDisplay/glyphs/ExonGlyph.d.ts.map +1 -1
  34. package/dist/LinearApolloDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  35. package/dist/LinearApolloDisplay/glyphs/TranscriptGlyph.d.ts.map +1 -1
  36. package/dist/LinearApolloDisplay/stateModel/base.d.ts +1 -18
  37. package/dist/LinearApolloDisplay/stateModel/base.d.ts.map +1 -1
  38. package/dist/LinearApolloDisplay/stateModel/index.d.ts +1 -17
  39. package/dist/LinearApolloDisplay/stateModel/index.d.ts.map +1 -1
  40. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts +2 -19
  41. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts.map +1 -1
  42. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts +2 -34
  43. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  44. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts +2 -19
  45. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts.map +1 -1
  46. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts +2 -0
  47. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts.map +1 -0
  48. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts +1 -1
  49. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts.map +1 -1
  50. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/index.d.ts +1 -1
  51. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/rendering.d.ts +1 -1
  52. package/dist/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.d.ts.map +1 -1
  53. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts +2 -0
  54. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts.map +1 -0
  55. package/dist/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  56. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts +1 -1
  57. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts.map +1 -1
  58. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts +1 -1
  59. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts.map +1 -1
  60. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts +2 -3
  61. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts.map +1 -1
  62. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts +2 -2
  63. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  64. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts +2 -3
  65. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts.map +1 -1
  66. package/dist/OntologyManager/OntologyStore/fulltext.d.ts.map +1 -1
  67. package/dist/OntologyManager/index.d.ts +734 -0
  68. package/dist/OntologyManager/index.d.ts.map +1 -0
  69. package/dist/TabularEditor/HybridGrid/NumberCell.d.ts.map +1 -1
  70. package/dist/components/AddCodingSequence.d.ts +14 -0
  71. package/dist/components/AddCodingSequence.d.ts.map +1 -0
  72. package/dist/components/AddRefSeqAliases.d.ts.map +1 -1
  73. package/dist/components/CopyFeature.d.ts.map +1 -1
  74. package/dist/components/CreateApolloAnnotation.d.ts.map +1 -1
  75. package/dist/components/DeleteFeature.d.ts.map +1 -1
  76. package/dist/components/DownloadGFF3.d.ts.map +1 -1
  77. package/dist/components/DuplicateFeatureDialog.d.ts +12 -0
  78. package/dist/components/DuplicateFeatureDialog.d.ts.map +1 -0
  79. package/dist/components/ImportAnnotations.d.ts +9 -0
  80. package/dist/components/ImportAnnotations.d.ts.map +1 -0
  81. package/dist/components/ManageUsers.d.ts.map +1 -1
  82. package/dist/components/OntologyTermMultiSelect.d.ts.map +1 -1
  83. package/dist/components/ViewChangeLog.d.ts.map +1 -1
  84. package/dist/components/index.d.ts +2 -0
  85. package/dist/components/index.d.ts.map +1 -1
  86. package/dist/extensions/annotationFromPileup.d.ts.map +1 -1
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.esm.js +830 -434
  89. package/dist/index.esm.js.map +1 -1
  90. package/dist/jbrowse-plugin-apollo.cjs.development.js +825 -429
  91. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  92. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  93. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  94. package/dist/jbrowse-plugin-apollo.umd.development.js +863 -468
  95. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  96. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  97. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  98. package/dist/session/ClientDataStore.d.ts +2194 -0
  99. package/dist/session/ClientDataStore.d.ts.map +1 -0
  100. package/dist/session/handleApolloFeaturesUrlParam.d.ts +8 -0
  101. package/dist/session/handleApolloFeaturesUrlParam.d.ts.map +1 -0
  102. package/dist/session/session.d.ts +6788 -0
  103. package/dist/session/session.d.ts.map +1 -0
  104. package/dist/util/displayUtils.d.ts +36 -0
  105. package/dist/util/displayUtils.d.ts.map +1 -0
  106. package/dist/util/sequenceUtils.d.ts +2 -0
  107. package/dist/util/sequenceUtils.d.ts.map +1 -0
  108. package/package.json +14 -14
  109. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +73 -38
  110. package/src/ApolloInternetAccount/components/LoginButtons.tsx +31 -10
  111. package/src/ApolloInternetAccount/model.ts +8 -5
  112. package/src/ApolloJobModel.ts +3 -8
  113. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +1 -1
  114. package/src/BackendDrivers/CollaborationServerDriver.ts +7 -11
  115. package/src/BackendDrivers/LocalDriver/LocalDriver.ts +13 -4
  116. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +1 -0
  117. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +4 -2
  118. package/src/FeatureDetailsWidget/Attributes.tsx +1 -0
  119. package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +1 -0
  120. package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +1 -0
  121. package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -0
  122. package/src/FeatureDetailsWidget/StringTextField.tsx +1 -0
  123. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +2 -1
  124. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +17 -354
  125. package/src/FeatureDetailsWidget/Translation.tsx +358 -0
  126. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +4 -1
  127. package/src/LinearApolloDisplay/glyphs/ExonGlyph.ts +2 -5
  128. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +4 -6
  129. package/src/LinearApolloDisplay/glyphs/TranscriptGlyph.ts +39 -6
  130. package/src/LinearApolloDisplay/stateModel/base.ts +47 -23
  131. package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -5
  132. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +4 -1
  133. package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -5
  134. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +10 -10
  135. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +5 -3
  136. package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +1 -1
  137. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +2 -3
  138. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +12 -20
  139. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +10 -6
  140. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +38 -45
  141. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +1 -4
  142. package/src/OntologyManager/OntologyStore/fulltext.ts +0 -2
  143. package/src/OntologyManager/OntologyStore/index.ts +1 -1
  144. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
  145. package/src/TabularEditor/HybridGrid/NumberCell.tsx +2 -1
  146. package/src/components/AddAssemblyAliases.tsx +1 -1
  147. package/src/components/AddCodingSequence.tsx +255 -0
  148. package/src/components/AddFeature.tsx +1 -1
  149. package/src/components/AddRefSeqAliases.tsx +3 -2
  150. package/src/components/CopyFeature.tsx +9 -7
  151. package/src/components/CreateApolloAnnotation.tsx +28 -23
  152. package/src/components/DeleteAssembly.tsx +2 -2
  153. package/src/components/DeleteFeature.tsx +1 -4
  154. package/src/components/DownloadGFF3.tsx +4 -3
  155. package/src/components/DuplicateFeatureDialog.tsx +154 -0
  156. package/src/components/DuplicateTranscript.tsx +1 -1
  157. package/src/components/FilterTranscripts.tsx +1 -1
  158. package/src/components/ImportAnnotations.tsx +108 -0
  159. package/src/components/ManageUsers.tsx +1 -2
  160. package/src/components/OntologyTermAutocomplete.tsx +1 -3
  161. package/src/components/OntologyTermMultiSelect.tsx +3 -1
  162. package/src/components/ViewChangeLog.tsx +2 -1
  163. package/src/components/index.ts +2 -0
  164. package/src/extensions/annotationFromPileup.ts +34 -26
  165. package/src/index.ts +9 -8
  166. package/src/session/ClientDataStore.ts +16 -21
  167. package/src/session/handleApolloFeaturesUrlParam.ts +122 -0
  168. package/src/session/session.ts +49 -9
  169. package/src/util/annotationFeatureUtils.ts +1 -1
  170. package/src/util/displayUtils.ts +1 -1
  171. package/src/util/glyphUtils.ts +3 -3
  172. package/src/util/sequenceUtils.ts +29 -0
@@ -41,6 +41,7 @@ var xDataGrid = require('@mui/x-data-grid');
41
41
  var gff = require('@gmod/gff');
42
42
  var fileSaver = require('file-saver');
43
43
  var AccountCircleIcon = require('@mui/icons-material/AccountCircle');
44
+ var BusinessIcon = require('@mui/icons-material/Business');
44
45
  var AdapterType = require('@jbrowse/core/pluggableElementTypes/AdapterType');
45
46
  var BaseAdapter = require('@jbrowse/core/data_adapters/BaseAdapter');
46
47
  var nanoid = require('nanoid');
@@ -53,9 +54,9 @@ var MoreHorizIcon = require('@mui/icons-material/MoreHoriz');
53
54
  var AddBoxIcon = require('@mui/icons-material/AddBox');
54
55
  var mst$1 = require('@apollo-annotation/mst');
55
56
  var styled = require('@emotion/styled');
57
+ var RemoveIcon = require('@mui/icons-material/Remove');
56
58
  var ContentCopyIcon = require('@mui/icons-material/ContentCopy');
57
59
  var ContentCutIcon = require('@mui/icons-material/ContentCut');
58
- var RemoveIcon = require('@mui/icons-material/Remove');
59
60
  var ClearIcon = require('@mui/icons-material/Clear');
60
61
  var UnfoldLessIcon = require('@mui/icons-material/UnfoldLess');
61
62
  var tracks = require('@jbrowse/core/util/tracks');
@@ -69,7 +70,7 @@ var RedoIcon = require('@mui/icons-material/Redo');
69
70
  var UndoIcon = require('@mui/icons-material/Undo');
70
71
  var SaveIcon = require('@mui/icons-material/Save');
71
72
 
72
- var version = "1.0.0";
73
+ var version = "1.1.0";
73
74
 
74
75
  const ApolloConfigSchema = configuration.ConfigurationSchema('ApolloInternetAccount', {
75
76
  baseURL: {
@@ -1372,9 +1373,7 @@ async function getValidTerms(ontologyStore, fetchValidTerms, filterTerms, signal
1372
1373
  result = customTermList;
1373
1374
  }
1374
1375
  }
1375
- if (!result) {
1376
- result = await ontologyStore.getAllTerms();
1377
- }
1376
+ result ??= await ontologyStore.getAllTerms();
1378
1377
  return filterTerms ? result.filter((element) => filterTerms(element)) : result;
1379
1378
  }
1380
1379
 
@@ -1555,7 +1554,7 @@ function CopyFeature({ changeManager, handleClose, session, sourceAssemblyId, so
1555
1554
  setErrorMessage('No assemblies to copy to');
1556
1555
  return;
1557
1556
  }
1558
- const assembly = await assemblyManager.waitForAssembly(selectedAssemblyId);
1557
+ const assembly = (await assemblyManager.waitForAssembly(selectedAssemblyId));
1559
1558
  if (!assembly) {
1560
1559
  return;
1561
1560
  }
@@ -1584,7 +1583,7 @@ function CopyFeature({ changeManager, handleClose, session, sourceAssemblyId, so
1584
1583
  event.preventDefault();
1585
1584
  setErrorMessage('');
1586
1585
  const featureLength = sourceFeature.length;
1587
- const assembly = await assemblyManager.waitForAssembly(selectedAssemblyId);
1586
+ const assembly = (await assemblyManager.waitForAssembly(selectedAssemblyId));
1588
1587
  if (!assembly) {
1589
1588
  setErrorMessage(`Assembly not found: ${selectedAssemblyId}.`);
1590
1589
  return;
@@ -1922,8 +1921,7 @@ function DeleteFeature({ changeManager, handleClose, selectedFeature, session, s
1922
1921
  locationChanges.push(geneChange);
1923
1922
  }
1924
1923
  }
1925
- if (featureTypeOntology &&
1926
- featureTypeOntology.isTypeOf(sourceFeature.type, 'exon')) {
1924
+ if (featureTypeOntology?.isTypeOf(sourceFeature.type, 'exon')) {
1927
1925
  const cdsChange = trimCDS(sourceFeature);
1928
1926
  if (cdsChange) {
1929
1927
  if (cdsChange.typeName === 'DeleteFeatureChange') {
@@ -2176,7 +2174,7 @@ function getOverlappingEdge(feature, x, minMax) {
2176
2174
  return;
2177
2175
  }
2178
2176
  function isSelectedFeature(feature, selectedFeature) {
2179
- return Boolean(selectedFeature && feature._id === selectedFeature._id);
2177
+ return feature._id === selectedFeature?._id;
2180
2178
  }
2181
2179
  function makeFeatureLabel(feature) {
2182
2180
  let name;
@@ -2782,6 +2780,153 @@ function AddAssemblyAliases({ changeManager, handleClose, session, }) {
2782
2780
  }, pageSizeOptions: [5], processRowUpdate: processRowUpdate, disableRowSelectionOnClick: true }) }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
2783
2781
  }
2784
2782
 
2783
+ const START_CODON = 'ATG';
2784
+ const STOP_CODONS = new Set(['TAA', 'TAG', 'TGA']);
2785
+ function findLongestOrf(sequence) {
2786
+ const starts = [
2787
+ null,
2788
+ null,
2789
+ null,
2790
+ ];
2791
+ let longest;
2792
+ for (let start = 0; start < sequence.length - 2; start++) {
2793
+ const codon = sequence.slice(start, start + 3).toUpperCase();
2794
+ const frame = (start % 3);
2795
+ if (codon === START_CODON && starts[frame] === null) {
2796
+ starts[frame] = start;
2797
+ }
2798
+ else if (STOP_CODONS.has(codon)) {
2799
+ const inFrameStart = starts[frame];
2800
+ if (inFrameStart === null) {
2801
+ continue;
2802
+ }
2803
+ const length = start - inFrameStart;
2804
+ if (!longest || longest[1] - longest[0] < length) {
2805
+ longest = [inFrameStart, start];
2806
+ }
2807
+ starts[frame] = null;
2808
+ }
2809
+ }
2810
+ return longest;
2811
+ }
2812
+
2813
+ // Returns the genomic half-open boundary for stitched position `pos`.
2814
+ // For strand +1: the inclusive start (min) of the nucleotide.
2815
+ // For strand -1: the exclusive end (max) of the nucleotide.
2816
+ // Symmetric usage:
2817
+ // plus: cdsMin = helper(orf[0]), cdsMax = helper(orf[1]+3)
2818
+ // minus: cdsMax = helper(orf[0]), cdsMin = helper(orf[1]+3)
2819
+ function stitchedToGenomicBound(pos, exons, strand) {
2820
+ let cum = 0;
2821
+ for (const exon of exons) {
2822
+ const len = exon.max - exon.min;
2823
+ if (pos <= cum + len) {
2824
+ const off = pos - cum;
2825
+ return strand === 1 ? exon.min + off : exon.max - off;
2826
+ }
2827
+ cum += len;
2828
+ }
2829
+ const last = exons.at(-1);
2830
+ if (!last) {
2831
+ throw new Error('No exons found');
2832
+ }
2833
+ return strand === 1 ? last.max : last.min;
2834
+ }
2835
+ function AddCodingSequence({ changeManager, handleClose, refName, session, sourceAssemblyId, sourceFeature, }) {
2836
+ const [method, setMethod] = React.useState('longest-orf');
2837
+ const [minInput, setMinInput] = React.useState('');
2838
+ const [maxInput, setMaxInput] = React.useState('');
2839
+ const [errorMessage, setErrorMessage] = React.useState('');
2840
+ async function onSubmit(event) {
2841
+ event.preventDefault();
2842
+ setErrorMessage('');
2843
+ let cdsMin;
2844
+ let cdsMax;
2845
+ try {
2846
+ if (method === 'longest-orf') {
2847
+ const exonParts = sourceFeature.transcriptExonParts.filter((p) => p.type === 'exon');
2848
+ if (exonParts.length === 0) {
2849
+ setErrorMessage('No exons found in this transcript');
2850
+ return;
2851
+ }
2852
+ const backendDriver = session.apolloDataStore.getBackendDriver(sourceAssemblyId);
2853
+ if (!backendDriver) {
2854
+ setErrorMessage('No backend driver found for this assembly');
2855
+ return;
2856
+ }
2857
+ let stitchedSequence = '';
2858
+ for (const exon of exonParts) {
2859
+ const { seq } = await backendDriver.getSequence({
2860
+ assemblyName: sourceAssemblyId,
2861
+ refName,
2862
+ start: exon.min,
2863
+ end: exon.max,
2864
+ });
2865
+ stitchedSequence += sourceFeature.strand === -1 ? util.revcom(seq) : seq;
2866
+ }
2867
+ const orf = findLongestOrf(stitchedSequence);
2868
+ if (!orf) {
2869
+ ;
2870
+ session.notify('No open reading frame found in this transcript', 'error');
2871
+ handleClose();
2872
+ return;
2873
+ }
2874
+ const strand = sourceFeature.strand ?? 1;
2875
+ if (strand === 1) {
2876
+ cdsMin = stitchedToGenomicBound(orf[0], exonParts, 1);
2877
+ cdsMax = stitchedToGenomicBound(orf[1] + 3, exonParts, 1);
2878
+ }
2879
+ else {
2880
+ cdsMax = stitchedToGenomicBound(orf[0], exonParts, -1);
2881
+ cdsMin = stitchedToGenomicBound(orf[1] + 3, exonParts, -1);
2882
+ }
2883
+ }
2884
+ else {
2885
+ cdsMin = Number(minInput) - 1;
2886
+ cdsMax = Number(maxInput);
2887
+ }
2888
+ const _id = new ObjectID().toHexString();
2889
+ const addedFeature = {
2890
+ _id,
2891
+ refSeq: sourceFeature.refSeq,
2892
+ min: cdsMin,
2893
+ max: cdsMax,
2894
+ type: 'CDS',
2895
+ };
2896
+ if (sourceFeature.strand) {
2897
+ addedFeature.strand = sourceFeature.strand;
2898
+ }
2899
+ const change = new shared.AddFeatureChange({
2900
+ changedIds: [sourceFeature._id],
2901
+ typeName: 'AddFeatureChange',
2902
+ assembly: sourceAssemblyId,
2903
+ addedFeature,
2904
+ parentFeatureId: sourceFeature._id,
2905
+ });
2906
+ await changeManager.submit(change);
2907
+ session.apolloSetSelectedFeature(_id);
2908
+ handleClose();
2909
+ }
2910
+ catch (error) {
2911
+ setErrorMessage(error instanceof Error ? error.message : 'An unknown error occurred');
2912
+ }
2913
+ }
2914
+ const manualError = method === 'manual' &&
2915
+ Boolean(minInput) &&
2916
+ Boolean(maxInput) &&
2917
+ Number(maxInput) <= Number(minInput);
2918
+ const submitDisabled = method === 'manual' ? !(minInput && maxInput) || manualError : false;
2919
+ return (jsxRuntime.jsxs(Dialog, { open: true, title: "Add coding sequence", handleClose: handleClose, maxWidth: false, "data-testid": "add-coding-sequence", children: [jsxRuntime.jsxs("form", { onSubmit: (event) => {
2920
+ void onSubmit(event);
2921
+ }, children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column' }, children: [jsxRuntime.jsx(material.FormControl, { children: jsxRuntime.jsxs(material.RadioGroup, { value: method, onChange: (e) => {
2922
+ setMethod(e.target.value);
2923
+ }, children: [jsxRuntime.jsx(material.FormControlLabel, { value: "longest-orf", control: jsxRuntime.jsx(material.Radio, {}), label: "Calculate longest open reading frame" }), jsxRuntime.jsx(material.FormControlLabel, { value: "manual", control: jsxRuntime.jsx(material.Radio, {}), label: "Manual" })] }) }), method === 'manual' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.TextField, { margin: "dense", id: "cds-min", label: "Min", type: "number", fullWidth: true, variant: "outlined", value: minInput, onChange: (e) => {
2924
+ setMinInput(e.target.value);
2925
+ } }), jsxRuntime.jsx(material.TextField, { margin: "dense", id: "cds-max", label: "Max", type: "number", fullWidth: true, variant: "outlined", value: maxInput, onChange: (e) => {
2926
+ setMaxInput(e.target.value);
2927
+ }, error: manualError, helperText: manualError ? '"Max" must be greater than "Min"' : null })] })) : null] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", type: "submit", disabled: submitDisabled, children: "Submit" }), jsxRuntime.jsx(material.Button, { variant: "outlined", type: "button", onClick: handleClose, children: "Cancel" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
2928
+ }
2929
+
2785
2930
  class BackendDriver {
2786
2931
  clientStore;
2787
2932
  constructor(clientStore) {
@@ -2854,7 +2999,7 @@ class LocalDriver extends BackendDriver {
2854
2999
  async getSequence(region) {
2855
3000
  const session = util.getSession(this.clientStore);
2856
3001
  const { assemblyManager } = session;
2857
- const assembly = await assemblyManager.waitForAssembly(region.assemblyName);
3002
+ const assembly = (await assemblyManager.waitForAssembly(region.assemblyName));
2858
3003
  if (!assembly) {
2859
3004
  throw new Error(`Assembly not found: "${region.assemblyName}"`);
2860
3005
  }
@@ -2873,7 +3018,10 @@ class LocalDriver extends BackendDriver {
2873
3018
  const adapter = new CLASS(
2874
3019
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
2875
3020
  adapterConf, undefined, pluginManager);
2876
- const seq = await adapter.getSequence(region);
3021
+ const seq = await adapter.getSequence({
3022
+ ...region,
3023
+ refName: assembly.getSeqAdapterRefName(region.refName),
3024
+ });
2877
3025
  if (!seq) {
2878
3026
  throw new Error(`Sequence not found: ${JSON.stringify(region)}`);
2879
3027
  }
@@ -2882,7 +3030,7 @@ class LocalDriver extends BackendDriver {
2882
3030
  async getRegions(assemblyName) {
2883
3031
  const session = util.getSession(this.clientStore);
2884
3032
  const { assemblyManager } = session;
2885
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
3033
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
2886
3034
  if (!assembly) {
2887
3035
  throw new Error(`Assembly not found: "${assemblyName}"`);
2888
3036
  }
@@ -2898,7 +3046,7 @@ class LocalDriver extends BackendDriver {
2898
3046
  async getRefNameAliases(assemblyName) {
2899
3047
  const session = util.getSession(this.clientStore);
2900
3048
  const { assemblyManager } = session;
2901
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
3049
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
2902
3050
  if (!assembly) {
2903
3051
  throw new Error(`Assembly not found: "${assemblyName}"`);
2904
3052
  }
@@ -3557,7 +3705,7 @@ class CollaborationServerDriver extends BackendDriver {
3557
3705
  const localSessionId = shared.makeUserSessionId(token);
3558
3706
  const changeManager = new ChangeManager(this.clientStore);
3559
3707
  // Save server last change sequence into session storage
3560
- internetAccount.setLastChangeSequenceNumber(Number(message.changeSequence));
3708
+ internetAccount.setLastChangeSequenceNumber(message.changeSequence);
3561
3709
  if (message.userSessionId === localSessionId) {
3562
3710
  return; // we did this change, no need to apply it again
3563
3711
  }
@@ -3605,14 +3753,10 @@ class CollaborationServerDriver extends BackendDriver {
3605
3753
  const seq = await inFlightPromise;
3606
3754
  return { seq, refSeq };
3607
3755
  }
3608
- let apolloAssembly = this.clientStore.assemblies.get(assemblyName);
3609
- if (!apolloAssembly) {
3610
- apolloAssembly = this.clientStore.addAssembly(assemblyName);
3611
- }
3612
- let apolloRefSeq = apolloAssembly.refSeqs.get(refSeq);
3613
- if (!apolloRefSeq) {
3614
- apolloRefSeq = apolloAssembly.addRefSeq(refSeq, refName);
3615
- }
3756
+ const apolloAssembly = this.clientStore.assemblies.get(assemblyName) ??
3757
+ this.clientStore.addAssembly(assemblyName);
3758
+ const apolloRefSeq = apolloAssembly.refSeqs.get(refSeq) ??
3759
+ apolloAssembly.addRefSeq(refSeq, refName);
3616
3760
  const clientStoreSequence = apolloRefSeq.getSequence(start, end);
3617
3761
  if (clientStoreSequence.length === end - start) {
3618
3762
  return { seq: clientStoreSequence, refSeq };
@@ -3969,7 +4113,7 @@ function AddFeature({ changeManager, handleClose, region, session, }) {
3969
4113
  setType(NewFeature[value]);
3970
4114
  }
3971
4115
  };
3972
- let submitDisabled = Boolean(error) || !(start && end && type);
4116
+ let submitDisabled = error || !(start && end && type);
3973
4117
  if ((type === NewFeature.CUSTOM && !customType) ||
3974
4118
  (!strand && type === NewFeature.GENE_AND_SUBFEATURES) ||
3975
4119
  (!strand && type === NewFeature.TRANSCRIPT_AND_SUBFEATURES)) {
@@ -3990,7 +4134,7 @@ function AddFeature({ changeManager, handleClose, region, session, }) {
3990
4134
  function DeleteAssembly({ changeManager, handleClose, session, }) {
3991
4135
  const { internetAccounts } = mobxStateTree.getRoot(session);
3992
4136
  const [errorMessage, setErrorMessage] = React.useState('');
3993
- const [confirmDelete, setconfirmDelete] = React.useState(false);
4137
+ const [confirmDelete, setConfirmDelete] = React.useState(false);
3994
4138
  const [submitted, setSubmitted] = React.useState(false);
3995
4139
  const apolloInternetAccounts = internetAccounts.filter((ia) => ia.type === 'ApolloInternetAccount');
3996
4140
  if (apolloInternetAccounts.length === 0) {
@@ -4031,7 +4175,7 @@ function DeleteAssembly({ changeManager, handleClose, session, }) {
4031
4175
  event.preventDefault();
4032
4176
  }
4033
4177
  return (jsxRuntime.jsxs(Dialog, { open: true, title: "Delete Assembly", handleClose: handleClose, maxWidth: false, "data-testid": "delete-assembly", children: [jsxRuntime.jsxs("form", { onSubmit: onSubmit, children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column' }, children: [apolloInternetAccounts.length > 1 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select account" }), jsxRuntime.jsx(material.Select, { value: selectedInternetAccount.internetAccountId, onChange: handleChangeInternetAccount, disabled: submitted && !errorMessage, children: internetAccounts.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.internetAccountId, children: option.name }, option.id))) })] })) : null, jsxRuntime.jsx(material.DialogContentText, { children: "Select assembly" }), jsxRuntime.jsx(material.Select, { labelId: "label", value: selectedAssembly?.name ?? '', onChange: handleChangeAssembly, disabled: assemblies.length === 0, children: assemblies.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.name, children: option.displayName }, option.name))) }), jsxRuntime.jsx(material.DialogContentText, { children: jsxRuntime.jsx("strong", { style: { color: 'red' }, children: "NOTE: All assembly data will be deleted and this operation cannot be undone!" }) }), jsxRuntime.jsx(material.FormGroup, { children: jsxRuntime.jsx(material.FormControlLabel, { control: jsxRuntime.jsx(material.Checkbox, { checked: confirmDelete, onChange: () => {
4034
- setconfirmDelete(!confirmDelete);
4178
+ setConfirmDelete(!confirmDelete);
4035
4179
  } }), label: "I understand that all assembly data will be deleted" }) })] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { disabled: !selectedAssembly || !confirmDelete, variant: "contained", type: "submit", children: "Delete" }), jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: handleClose, children: "Cancel" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
4036
4180
  }
4037
4181
 
@@ -4040,7 +4184,7 @@ function Export(props) {
4040
4184
  return (jsxRuntime.jsx(material.SvgIcon, { viewBox: "0 0 24 24", ...props, children: jsxRuntime.jsx("path", { d: "M23,12L19,8V11H10V13H19V16M1,18V6C1,4.89 1.9,4 3,4H15A2,2 0 0,1 17,6V9H15V6H3V18H15V15H17V18A2,2 0 0,1 15,20H3A2,2 0 0,1 1,18Z" }) }));
4041
4185
  }
4042
4186
  function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
4043
- const [includeFASTA, setincludeFASTA] = React.useState(false);
4187
+ const [includeFASTA, setIncludeFASTA] = React.useState(false);
4044
4188
  const [errorMessage, setErrorMessage] = React.useState('');
4045
4189
  const { getInternetAccount } = session.apolloDataStore;
4046
4190
  const { assemblyManager } = session;
@@ -4089,7 +4233,7 @@ function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
4089
4233
  window.open(exportUri, '_blank');
4090
4234
  }
4091
4235
  return (jsxRuntime.jsxs(Dialog, { open: true, title: "Export annotations", handleClose: handleClose, maxWidth: false, "data-testid": "download-gff3", children: [jsxRuntime.jsxs("form", { onSubmit: onSubmit, children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column' }, children: [jsxRuntime.jsxs(material.DialogContentText, { children: ["Exporting annotations for ", assemblyName] }), jsxRuntime.jsx(material.FormGroup, { children: jsxRuntime.jsx(material.FormControlLabel, { "data-testid": "include-fasta-checkbox", control: jsxRuntime.jsx(material.Checkbox, { checked: includeFASTA, onChange: () => {
4092
- setincludeFASTA(!includeFASTA);
4236
+ setIncludeFASTA(!includeFASTA);
4093
4237
  }, disabled: !internetAccountConfigId }), label: "Include fasta sequence in GFF output" }) })] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", type: "submit", children: "Download" }), jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: handleClose, children: "Cancel" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
4094
4238
  }
4095
4239
  function getAssemblyGFF3Stream(assemblyName) {
@@ -4128,6 +4272,49 @@ async function* getFeaturesForAssembly(assemblyName) {
4128
4272
  db.close();
4129
4273
  }
4130
4274
 
4275
+ function ImportAnnotations({ assemblyName, handleClose, session, }) {
4276
+ const { changeManager } = session.apolloDataStore;
4277
+ const [file, setFile] = React.useState();
4278
+ const [errorMessage, setErrorMessage] = React.useState('');
4279
+ const [loading, setLoading] = React.useState(false);
4280
+ function handleChangeFile(e) {
4281
+ if (e.target.files) {
4282
+ setFile(e.target.files[0]);
4283
+ }
4284
+ }
4285
+ async function onSubmit(event) {
4286
+ event.preventDefault();
4287
+ if (!file) {
4288
+ return;
4289
+ }
4290
+ setLoading(true);
4291
+ setErrorMessage('');
4292
+ try {
4293
+ const stream = file
4294
+ .stream()
4295
+ .pipeThrough(new TransformStream(new gff.GFFTransformer({ parseSequences: false })));
4296
+ for await (const value of stream) {
4297
+ const annotationFeature = shared.gff3ToAnnotationFeature(value);
4298
+ const change = new shared.AddFeatureChange({
4299
+ changedIds: [annotationFeature._id],
4300
+ typeName: 'AddFeatureChange',
4301
+ assembly: assemblyName,
4302
+ addedFeature: annotationFeature,
4303
+ });
4304
+ await changeManager.submit(change);
4305
+ }
4306
+ handleClose();
4307
+ }
4308
+ catch (error) {
4309
+ setErrorMessage(String(error));
4310
+ }
4311
+ finally {
4312
+ setLoading(false);
4313
+ }
4314
+ }
4315
+ return (jsxRuntime.jsxs(Dialog, { open: true, title: "Import annotations", handleClose: handleClose, children: [loading ? jsxRuntime.jsx(material.LinearProgress, {}) : null, jsxRuntime.jsxs("form", { onSubmit: onSubmit, children: [jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Import annotations from a GFF3 file into the current Apollo track. Features are parsed and stored locally in the browser, so importing very large GFF3 files is not recommended." }), jsxRuntime.jsx("input", { type: "file", accept: ".gff,.gff3", onChange: handleChangeFile, disabled: loading }), errorMessage ? (jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage })) : null] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", type: "submit", disabled: !file || loading, children: "Import" }), jsxRuntime.jsx(material.Button, { variant: "outlined", onClick: handleClose, children: "Cancel" })] })] })] }));
4316
+ }
4317
+
4131
4318
  function ImportFeatures({ changeManager, handleClose, session, }) {
4132
4319
  const { apolloDataStore } = session;
4133
4320
  const [file, setFile] = React.useState();
@@ -4531,7 +4718,7 @@ function ManageUsers({ changeManager, handleClose, session, }) {
4531
4718
  });
4532
4719
  return newRow;
4533
4720
  }
4534
- return (jsxRuntime.jsxs(Dialog, { open: true, fullScreen: true, title: "Manage users", handleClose: handleClose, "data-testid": "manage-users", children: [jsxRuntime.jsxs(material.DialogContent, { children: [apolloInternetAccounts.length > 1 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select account" }), jsxRuntime.jsx(material.Select, { value: selectedInternetAccount.internetAccountId, onChange: handleChangeInternetAccount, disabled: !errorMessage, children: internetAccounts.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.internetAccountId, children: option.name }, option.id))) })] })) : null, jsxRuntime.jsx("div", { style: { height: '100%', width: '100%' }, children: jsxRuntime.jsx(xDataGrid.DataGrid, { pagination: true, rows: users, columns: gridColumns, getRowId: (row) => row._id, slots: { toolbar: xDataGrid.GridToolbar }, getRowHeight: () => 'auto', isCellEditable: (params) => !isCurrentUser(params.id), processRowUpdate: processRowUpdate, onProcessRowUpdateError: (error) => {
4721
+ return (jsxRuntime.jsxs(Dialog, { open: true, fullScreen: true, title: "Manage users", handleClose: handleClose, "data-testid": "manage-users", children: [jsxRuntime.jsxs(material.DialogContent, { children: [apolloInternetAccounts.length > 1 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select account" }), jsxRuntime.jsx(material.Select, { value: selectedInternetAccount.internetAccountId, onChange: handleChangeInternetAccount, disabled: !errorMessage, children: internetAccounts.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.internetAccountId, children: option.name }, option.id))) })] })) : null, jsxRuntime.jsx("div", { style: { height: '100%', width: '100%' }, children: jsxRuntime.jsx(xDataGrid.DataGrid, { pagination: true, rows: users, columns: gridColumns, getRowId: (row) => row._id, showToolbar: true, getRowHeight: () => 'auto', isCellEditable: (params) => !isCurrentUser(params.id), processRowUpdate: processRowUpdate, onProcessRowUpdateError: (error) => {
4535
4722
  setErrorMessage(String(error));
4536
4723
  } }) })] }), jsxRuntime.jsx(material.DialogActions, { children: jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: handleClose, children: "Close" }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
4537
4724
  }
@@ -4841,12 +5028,13 @@ const AddRefSeqAliases = mobxReact.observer(function AddRefSeqAliases({ changeMa
4841
5028
  const [enableSubmit, setEnableSubmit] = React.useState(false);
4842
5029
  const [selectedAssembly, setSelectedAssembly] = React.useState();
4843
5030
  const [selectedRows, setSelectedRows] = React.useState([]);
4844
- const [refNameAliasMap, setRefNameAliasMap] = React.useState(new Map());
5031
+ const [refNameAliasMap, setRefNameAliasMap] = React.useState(() => new Map());
4845
5032
  const { apolloDataStore } = session;
4846
5033
  const { collaborationServerDriver } = apolloDataStore;
4847
5034
  const assemblies = collaborationServerDriver.getAssemblies();
4848
5035
  React.useEffect(() => {
4849
5036
  if (assemblies.length > 0) {
5037
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
4850
5038
  setSelectedAssembly(assemblies[0]);
4851
5039
  collaborationServerDriver
4852
5040
  .getRefNameAliases(assemblies[0].name)
@@ -4858,7 +5046,7 @@ const AddRefSeqAliases = mobxReact.observer(function AddRefSeqAliases({ changeMa
4858
5046
  setErrorMessage('Error fetching refName aliases for assembly');
4859
5047
  });
4860
5048
  }
4861
- // eslint-disable-next-line react-hooks/exhaustive-deps
5049
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
4862
5050
  }, []);
4863
5051
  const initializeRefNameAliasMap = (refNameAliasesList) => {
4864
5052
  const initialMap = new Map();
@@ -5263,17 +5451,23 @@ const useStyles$d = tssReact.makeStyles()((theme) => ({
5263
5451
  }));
5264
5452
  function GoogleButton(props) {
5265
5453
  const { classes } = useStyles$d();
5266
- const { disabled } = props;
5267
- return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Google, { color: disabled ? 'disabled' : undefined }), ...props, children: "Sign in with Google" }));
5454
+ const { message } = props;
5455
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Google, {}), ...props, children: message }));
5268
5456
  }
5269
5457
  function MicrosoftButton(props) {
5270
5458
  const { classes } = useStyles$d();
5271
- const { disabled } = props;
5272
- return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Microsoft, { color: disabled ? 'disabled' : undefined }), ...props, children: "Sign in with Microsoft" }));
5459
+ const { message } = props;
5460
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Microsoft, {}), ...props, children: message }));
5273
5461
  }
5274
5462
  function GuestButton(props) {
5275
5463
  const { classes } = useStyles$d();
5276
- return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(AccountCircleIcon, { fontSize: "small" }), ...props, children: "Continue as Guest" }));
5464
+ const { message } = props;
5465
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(AccountCircleIcon, { fontSize: "small" }), ...props, children: message }));
5466
+ }
5467
+ function GenericButton(props) {
5468
+ const { classes } = useStyles$d();
5469
+ const { message } = props;
5470
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(BusinessIcon, { fontSize: "small" }), ...props, children: message }));
5277
5471
  }
5278
5472
 
5279
5473
  const useStyles$c = tssReact.makeStyles()((theme) => ({
@@ -5285,7 +5479,7 @@ const useStyles$c = tssReact.makeStyles()((theme) => ({
5285
5479
  const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
5286
5480
  const { classes } = useStyles$c();
5287
5481
  const [errorMessage, setErrorMessage] = React.useState('');
5288
- const [loginTypes, setLoginTypes] = React.useState([]);
5482
+ const [loginTypes, setLoginTypes] = React.useState(null);
5289
5483
  React.useEffect(() => {
5290
5484
  const controller = new AbortController();
5291
5485
  const { signal } = controller;
@@ -5299,6 +5493,9 @@ const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
5299
5493
  }
5300
5494
  const data = (await response.json());
5301
5495
  setLoginTypes(data);
5496
+ if (data.length === 0) {
5497
+ setErrorMessage('No login types configured');
5498
+ }
5302
5499
  }
5303
5500
  getAuthTypes().catch((error) => {
5304
5501
  if (!aborting.isAbortException(error)) {
@@ -5310,26 +5507,35 @@ const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
5310
5507
  };
5311
5508
  }, [baseURL]);
5312
5509
  function handleClick(authType) {
5313
- if (authType === 'google') {
5314
- handleClose('google');
5315
- }
5316
- else if (authType === 'microsoft') {
5317
- handleClose('microsoft');
5318
- }
5319
- else {
5320
- handleClose('guest');
5321
- }
5322
- }
5323
- const allowGoogle = loginTypes.includes('google');
5324
- const allowMicrosoft = loginTypes.includes('microsoft');
5325
- const allowGuest = loginTypes.includes('guest');
5326
- return (jsxRuntime.jsxs(Dialog, { open: true, title: `Log in to ${name}`, handleClose: handleClose, maxWidth: false, "data-testid": "login-apollo", children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column', paddingTop: 8 }, children: [allowGoogle ? (jsxRuntime.jsx(GoogleButton, { disabled: !allowGoogle, onClick: () => {
5327
- handleClick('google');
5328
- } })) : null, allowMicrosoft ? (jsxRuntime.jsx(MicrosoftButton, { disabled: !allowMicrosoft, onClick: () => {
5329
- handleClick('microsoft');
5330
- } })) : null, allowGuest ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.Divider, { className: classes.divider }), jsxRuntime.jsx(GuestButton, { onClick: () => {
5331
- handleClick('guest');
5332
- } })] })) : null] }), jsxRuntime.jsx(material.DialogActions, { children: jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: () => {
5510
+ handleClose(authType);
5511
+ }
5512
+ if (loginTypes === null) {
5513
+ return 'Loading…';
5514
+ }
5515
+ const firstLoginType = loginTypes.at(0);
5516
+ if (firstLoginType && loginTypes.length === 1 && !firstLoginType.needsPopup) {
5517
+ handleClick(firstLoginType);
5518
+ }
5519
+ return (jsxRuntime.jsxs(Dialog, { open: true, title: `Log in to ${name}`, handleClose: handleClose, maxWidth: false, "data-testid": "login-apollo", children: [jsxRuntime.jsx(material.DialogContent, { style: { display: 'flex', flexDirection: 'column', paddingTop: 8 }, children: loginTypes.map((loginType) => {
5520
+ if (loginType.name === 'google') {
5521
+ return (jsxRuntime.jsx(GoogleButton, { message: loginType.message, onClick: () => {
5522
+ handleClick(loginType);
5523
+ } }, loginType.name));
5524
+ }
5525
+ if (loginType.name === 'microsoft') {
5526
+ return (jsxRuntime.jsx(MicrosoftButton, { message: loginType.message, onClick: () => {
5527
+ handleClick(loginType);
5528
+ } }, loginType.name));
5529
+ }
5530
+ if (loginType.name === 'guest') {
5531
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(material.Divider, { className: classes.divider }), jsxRuntime.jsx(GuestButton, { message: loginType.message, onClick: () => {
5532
+ handleClick(loginType);
5533
+ } })] }, loginType.name));
5534
+ }
5535
+ return (jsxRuntime.jsx(GenericButton, { message: loginType.message, onClick: () => {
5536
+ handleClick(loginType);
5537
+ } }, loginType.name));
5538
+ }) }), jsxRuntime.jsx(material.DialogActions, { children: jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: () => {
5333
5539
  handleClose();
5334
5540
  }, children: "Cancel" }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
5335
5541
  };
@@ -5525,13 +5731,13 @@ const stateModelFactory$3 = (configSchema) => {
5525
5731
  },
5526
5732
  ]);
5527
5733
  });
5528
- if (authType !== 'guest') {
5734
+ if (authType.needsPopup) {
5529
5735
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
5530
- self.openAuthWindow(authType, resolve, reject);
5736
+ self.openAuthWindow(authType.name, resolve, reject);
5531
5737
  return;
5532
5738
  }
5533
5739
  const url = new URL('auth/login', baseURL);
5534
- const searchParams = new URLSearchParams({ type: authType });
5740
+ const searchParams = new URLSearchParams({ type: authType.name });
5535
5741
  url.search = searchParams.toString();
5536
5742
  const uri = url.toString();
5537
5743
  const response = await fetch(uri, { signal: self.controller.signal });
@@ -5652,7 +5858,7 @@ const stateModelFactory$3 = (configSchema) => {
5652
5858
  socket.on('COMMON', (message) => {
5653
5859
  if ('checkResult' in message) {
5654
5860
  if (message.deleted) {
5655
- deleteCheckResult(message.checkResult._id.toString());
5861
+ deleteCheckResult(message.checkResult._id);
5656
5862
  }
5657
5863
  else {
5658
5864
  addCheckResult(message.checkResult);
@@ -6272,7 +6478,9 @@ const DefaultAttributeEditor = mobxReact.observer(function DefaultAttributeEdito
6272
6478
  return newValues;
6273
6479
  });
6274
6480
  }
6275
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [newValues.map((value, idx) => (jsxRuntime.jsxs("div", { style: { display: 'flex' }, children: [jsxRuntime.jsx(StringTextField, { value: value, onChangeCommitted: (editedValue) => {
6481
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [newValues.map((value, idx) => (
6482
+ // eslint-disable-next-line @eslint-react/no-array-index-key
6483
+ jsxRuntime.jsxs("div", { style: { display: 'flex' }, children: [jsxRuntime.jsx(StringTextField, { value: value, onChangeCommitted: (editedValue) => {
6276
6484
  updateValue(idx, editedValue);
6277
6485
  }, variant: "outlined", fullWidth: true }), jsxRuntime.jsx(material.IconButton, { "aria-label": "delete", size: "medium", edge: "end", onClick: () => {
6278
6486
  deleteValue(idx);
@@ -6284,7 +6492,9 @@ const DefaultAttributeEditor = mobxReact.observer(function DefaultAttributeEdito
6284
6492
  });
6285
6493
 
6286
6494
  function DefaultAttributeViewer({ values }) {
6287
- return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: values?.map((value, idx) => (jsxRuntime.jsx(material.Typography, { variant: "body2", color: "textSecondary", children: value }, `${idx}.${value}`))) }));
6495
+ return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: values?.map((value, idx) => (jsxRuntime.jsx(material.Typography
6496
+ // eslint-disable-next-line @eslint-react/no-array-index-key
6497
+ , { variant: "body2", color: "textSecondary", children: value }, `${idx}.${value}`))) }));
6288
6498
  }
6289
6499
 
6290
6500
  const useStyles$a = tssReact.makeStyles()((theme) => ({
@@ -6590,6 +6800,7 @@ const ApolloFeatureDetailsWidget = mobxReact.observer(function ApolloFeatureDeta
6590
6800
  const { classes } = useStyles$8();
6591
6801
  const [panelState, setPanelState] = React.useState(['attributes']);
6592
6802
  React.useEffect(() => {
6803
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
6593
6804
  setPanelState(['attributes']);
6594
6805
  }, [feature]);
6595
6806
  if (!(feature && currentAssembly)) {
@@ -6891,7 +7102,7 @@ const TranscriptSequence = mobxReact.observer(function TranscriptSequence({ asse
6891
7102
  else {
6892
7103
  setSequenceOptions(defaultSequenceOptions);
6893
7104
  }
6894
- // eslint-disable-next-line react-hooks/exhaustive-deps
7105
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
6895
7106
  }, [feature]);
6896
7107
  if (!(currentAssembly && refData)) {
6897
7108
  return null;
@@ -6954,19 +7165,6 @@ const TranscriptSequence = mobxReact.observer(function TranscriptSequence({ asse
6954
7165
  .join(';'), "(strand=", feature.strand === 1 ? '+' : '-', ";length=", getSequenceLength(sequenceSegments), ")", jsxRuntime.jsx("br", {}), wrapSequence(sequenceSegments, SEQUENCE_WRAP_LENGTH)] })] }));
6955
7166
  });
6956
7167
 
6957
- const StyledTextField = styled(NumberTextField)(() => ({
6958
- '&.MuiFormControl-root': {
6959
- marginTop: 0,
6960
- marginBottom: 0,
6961
- width: '100%',
6962
- },
6963
- '& .MuiInputBase-input': {
6964
- fontSize: 12,
6965
- height: 20,
6966
- padding: 1,
6967
- paddingLeft: 10,
6968
- },
6969
- }));
6970
7168
  const SequenceContainer = styled('div')({
6971
7169
  display: 'flex',
6972
7170
  justifyContent: 'center',
@@ -6988,6 +7186,187 @@ const StyledAccordionSummary$1 = styled(material.AccordionSummary)(() => ({
6988
7186
  maxHeight: 30,
6989
7187
  },
6990
7188
  }));
7189
+ function Translation({ changeInProgress, cdsLocations, refData, strand,
7190
+ // eslint-disable-next-line @typescript-eslint/unbound-method
7191
+ updateCDSLocation, cdsMin, cdsMax, feature, session, }) {
7192
+ const seqRef = React.useRef(null);
7193
+ const { notify } = session;
7194
+ const cdsSequences = [];
7195
+ const [firstLocation] = cdsLocations;
7196
+ for (const loc of firstLocation) {
7197
+ const seq = refData.getSequence(loc.min, loc.max);
7198
+ cdsSequences.push(strand === -1 ? util.revcom(seq) : seq);
7199
+ }
7200
+ const cdsSequence = cdsSequences.join('');
7201
+ const proteinSequence = [];
7202
+ for (let codonGenomicPos = 0; codonGenomicPos < cdsSequence.length; codonGenomicPos += 3) {
7203
+ const codonSeq = cdsSequence
7204
+ .slice(codonGenomicPos, codonGenomicPos + 3)
7205
+ .toUpperCase();
7206
+ const protein = util.defaultCodonTable[codonSeq] || '&';
7207
+ proteinSequence.push(protein);
7208
+ }
7209
+ const onCopyClick = () => {
7210
+ const seqDiv = seqRef.current;
7211
+ if (!seqDiv) {
7212
+ return;
7213
+ }
7214
+ void copyToClipboard(seqDiv);
7215
+ };
7216
+ // Codon position is the index of the start codon in the CDS genomic sequence
7217
+ // Calculate the genomic location of the start codon based on the codon position in the CDS
7218
+ const getCodonGenomicLocation = (codonGenomicPosition) => {
7219
+ const [firstLocation] = cdsLocations;
7220
+ let cdsLen = 0;
7221
+ const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
7222
+ // Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
7223
+ // and codonGenomicPosition is 25
7224
+ // ((10 - 0) + (30 - 20) + (50 - 40)) > 25
7225
+ // So, start codon is in (40, 50)
7226
+ // 40 + (25-20) = 45 is the genomic location of the start codon
7227
+ if (strand === 1) {
7228
+ for (const loc of sortedCDSLocations) {
7229
+ const locLength = loc.max - loc.min;
7230
+ if (cdsLen + locLength > codonGenomicPosition) {
7231
+ return loc.min + (codonGenomicPosition - cdsLen);
7232
+ }
7233
+ cdsLen += locLength;
7234
+ }
7235
+ }
7236
+ else if (strand === -1) {
7237
+ for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
7238
+ const loc = sortedCDSLocations[i];
7239
+ const locLength = loc.max - loc.min;
7240
+ if (cdsLen + locLength > codonGenomicPosition) {
7241
+ return loc.max - (codonGenomicPosition - cdsLen);
7242
+ }
7243
+ cdsLen += locLength;
7244
+ }
7245
+ }
7246
+ if (strand === 1) {
7247
+ return cdsMin;
7248
+ }
7249
+ return cdsMax;
7250
+ };
7251
+ const trimTranslationSequence = () => {
7252
+ if ((proteinSequence.at(0) === 'M' && proteinSequence.at(-1) === '*') ||
7253
+ proteinSequence.length === 0) {
7254
+ return;
7255
+ }
7256
+ // Trim any sequence before first start codon and after stop codon
7257
+ const startCodonIndex = proteinSequence.indexOf('M');
7258
+ const stopCodonIndex = proteinSequence.lastIndexOf('*');
7259
+ const startCodonPos = startCodonIndex * 3;
7260
+ const stopCodonPos = stopCodonIndex * 3;
7261
+ const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
7262
+ let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
7263
+ if (strand === 1) {
7264
+ if (startCodonGenomicLoc > stopCodonGenomicLoc) {
7265
+ notify('Start codon genomic location should be less than stop codon genomic location', 'error');
7266
+ return;
7267
+ }
7268
+ let promise;
7269
+ stopCodonGenomicLoc += 3; // move to end of stop codon
7270
+ if (startCodonGenomicLoc !== cdsMin) {
7271
+ promise = new Promise((resolve) => {
7272
+ updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
7273
+ resolve(true);
7274
+ });
7275
+ });
7276
+ }
7277
+ if (stopCodonGenomicLoc !== cdsMax) {
7278
+ if (promise) {
7279
+ void promise.then(() => {
7280
+ updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
7281
+ });
7282
+ }
7283
+ else {
7284
+ updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
7285
+ }
7286
+ }
7287
+ }
7288
+ if (strand === -1) {
7289
+ // reverse strand
7290
+ if (startCodonGenomicLoc < stopCodonGenomicLoc) {
7291
+ notify('Start codon genomic location should be less than stop codon genomic location', 'error');
7292
+ return;
7293
+ }
7294
+ let promise;
7295
+ stopCodonGenomicLoc -= 3; // move to end of stop codon
7296
+ if (startCodonGenomicLoc !== cdsMax) {
7297
+ promise = new Promise((resolve) => {
7298
+ updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
7299
+ resolve(true);
7300
+ });
7301
+ });
7302
+ }
7303
+ if (stopCodonGenomicLoc !== cdsMin) {
7304
+ if (promise) {
7305
+ void promise.then(() => {
7306
+ updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
7307
+ });
7308
+ }
7309
+ else {
7310
+ updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
7311
+ }
7312
+ }
7313
+ }
7314
+ notify('Translation sequence trimmed to start and stop codons', 'success');
7315
+ };
7316
+ return (jsxRuntime.jsx("div", { children: jsxRuntime.jsxs(material.Accordion, { children: [jsxRuntime.jsx(StyledAccordionSummary$1, { expandIcon: jsxRuntime.jsx(ExpandMoreIcon, { style: { color: 'white' } }), "aria-controls": "panel1-content", id: "panel1-header", children: jsxRuntime.jsx(material.Typography, { component: "span", fontWeight: 'bold', children: "Translation" }) }), jsxRuntime.jsxs(material.AccordionDetails, { children: [jsxRuntime.jsx(SequenceContainer, { children: jsxRuntime.jsx(material.Typography, { component: 'span', ref: seqRef, style: { maxHeight: 120, overflowY: 'scroll' }, children: proteinSequence.map((protein, idx) => {
7317
+ const codonGenomicPos = idx * 3;
7318
+ if (protein === 'M') {
7319
+ return (jsxRuntime.jsx(material.Typography, { component: 'span', style: {
7320
+ backgroundColor: changeInProgress
7321
+ ? 'lightgray'
7322
+ : 'yellow',
7323
+ cursor: 'pointer',
7324
+ border: '1px solid black',
7325
+ }, onClick: () => {
7326
+ if (changeInProgress) {
7327
+ return;
7328
+ }
7329
+ // NOTE: codonGenomicPos is important here for calculating the genomic location
7330
+ // of the start codon. We are using the codonGenomicPos as the key in the typography
7331
+ // elements to maintain the genomic postion of the codon start
7332
+ const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
7333
+ if (startCodonGenomicLocation !== cdsMin &&
7334
+ strand === 1) {
7335
+ updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
7336
+ }
7337
+ if (startCodonGenomicLocation !== cdsMax &&
7338
+ strand === -1) {
7339
+ updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
7340
+ }
7341
+ }, children: protein }, codonGenomicPos));
7342
+ }
7343
+ if (protein === '*') {
7344
+ return (jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
7345
+ }
7346
+ // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
7347
+ return (jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
7348
+ }) }) }), jsxRuntime.jsxs("div", { style: {
7349
+ marginTop: 10,
7350
+ display: 'flex',
7351
+ flexDirection: 'row',
7352
+ alignItems: 'center',
7353
+ gap: 10,
7354
+ }, children: [jsxRuntime.jsx(material.Tooltip, { title: "Copy", children: jsxRuntime.jsx("button", { onClick: onCopyClick, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCopyIcon, { style: { fontSize: 15 } }) }) }), jsxRuntime.jsx(material.Tooltip, { title: "Trim", children: jsxRuntime.jsx("button", { onClick: trimTranslationSequence, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCutIcon, { style: { fontSize: 15 } }) }) })] })] })] }) }));
7355
+ }
7356
+
7357
+ const StyledTextField = styled(NumberTextField)(() => ({
7358
+ '&.MuiFormControl-root': {
7359
+ marginTop: 0,
7360
+ marginBottom: 0,
7361
+ width: '100%',
7362
+ },
7363
+ '& .MuiInputBase-input': {
7364
+ fontSize: 12,
7365
+ height: 20,
7366
+ padding: 1,
7367
+ paddingLeft: 10,
7368
+ },
7369
+ }));
6991
7370
  const Strand = (props) => {
6992
7371
  const { strand } = props;
6993
7372
  return (jsxRuntime.jsx("div", { children: strand === 1 ? (jsxRuntime.jsx(AddIcon, {})) : strand === -1 ? (jsxRuntime.jsx(RemoveIcon, {})) : (jsxRuntime.jsx(material.Typography, { component: 'span', children: "N/A" })) }));
@@ -7006,7 +7385,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7006
7385
  const currentAssembly = session.apolloDataStore.assemblies.get(assembly);
7007
7386
  const refData = currentAssembly?.getByRefName(refName);
7008
7387
  const { changeManager } = session.apolloDataStore;
7009
- const seqRef = React.useRef(null);
7010
7388
  const { changeInProgress } = session;
7011
7389
  if (!refData) {
7012
7390
  return null;
@@ -7433,184 +7811,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7433
7811
  },
7434
7812
  ];
7435
7813
  };
7436
- const getTranslationSequence = () => {
7437
- let wholeSequence = '';
7438
- const [firstLocation] = cdsLocations;
7439
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
7440
- for (const loc of sortedCDSLocations) {
7441
- wholeSequence += refData.getSequence(loc.min, loc.max);
7442
- }
7443
- if (strand === -1) {
7444
- // Original: ACGCAT
7445
- // Complement: TGCGTA
7446
- // Reverse complement: ATGCGT
7447
- wholeSequence = util.revcom(wholeSequence);
7448
- }
7449
- const elements = [];
7450
- for (let codonGenomicPos = 0; codonGenomicPos < wholeSequence.length; codonGenomicPos += 3) {
7451
- const codonSeq = wholeSequence
7452
- .slice(codonGenomicPos, codonGenomicPos + 3)
7453
- .toUpperCase();
7454
- const protein = util.defaultCodonTable[codonSeq] || '&';
7455
- // highlight start codon and stop codons
7456
- if (codonSeq === 'ATG') {
7457
- elements.push(jsxRuntime.jsx(material.Typography, { component: 'span', style: {
7458
- backgroundColor: changeInProgress ? 'lightgray' : 'yellow',
7459
- cursor: 'pointer',
7460
- border: '1px solid black',
7461
- }, onClick: () => {
7462
- if (changeInProgress) {
7463
- return;
7464
- }
7465
- // NOTE: codonGenomicPos is important here for calculating the genomic location
7466
- // of the start codon. We are using the codonGenomicPos as the key in the typography
7467
- // elements to maintain the genomic postion of the codon start
7468
- const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
7469
- if (startCodonGenomicLocation !== cdsMin && strand === 1) {
7470
- updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
7471
- }
7472
- if (startCodonGenomicLocation !== cdsMax && strand === -1) {
7473
- updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
7474
- }
7475
- }, children: protein }, codonGenomicPos));
7476
- }
7477
- else if (['TAA', 'TAG', 'TGA'].includes(codonSeq)) {
7478
- elements.push(jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
7479
- }
7480
- else {
7481
- elements.push(
7482
- // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
7483
- jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
7484
- }
7485
- }
7486
- return elements;
7487
- };
7488
- // Codon position is the index of the start codon in the CDS genomic sequence
7489
- // Calculate the genomic location of the start codon based on the codon position in the CDS
7490
- const getCodonGenomicLocation = (codonGenomicPosition) => {
7491
- const [firstLocation] = cdsLocations;
7492
- let cdsLen = 0;
7493
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
7494
- // Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
7495
- // and codonGenomicPosition is 25
7496
- // ((10 - 0) + (30 - 20) + (50 - 40)) > 25
7497
- // So, start codon is in (40, 50)
7498
- // 40 + (25-20) = 45 is the genomic location of the start codon
7499
- if (strand === 1) {
7500
- for (const loc of sortedCDSLocations) {
7501
- const locLength = loc.max - loc.min;
7502
- if (cdsLen + locLength > codonGenomicPosition) {
7503
- return loc.min + (codonGenomicPosition - cdsLen);
7504
- }
7505
- cdsLen += locLength;
7506
- }
7507
- }
7508
- else if (strand === -1) {
7509
- for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
7510
- const loc = sortedCDSLocations[i];
7511
- const locLength = loc.max - loc.min;
7512
- if (cdsLen + locLength > codonGenomicPosition) {
7513
- return loc.max - (codonGenomicPosition - cdsLen);
7514
- }
7515
- cdsLen += locLength;
7516
- }
7517
- }
7518
- if (strand === 1) {
7519
- return cdsMin;
7520
- }
7521
- return cdsMax;
7522
- };
7523
- const trimTranslationSequence = () => {
7524
- const sequenceElements = getTranslationSequence();
7525
- const translationSequence = sequenceElements
7526
- .map((el) => el.props.children)
7527
- .join('');
7528
- if (translationSequence.startsWith('M') &&
7529
- translationSequence.endsWith('*')) {
7530
- return;
7531
- }
7532
- // NOTE: We are maintaining the genomic location of the codon start as the "key"
7533
- // in typography elements. See getTranslationSequence function
7534
- const translSeqCodonStartGenomicPosArr = [];
7535
- for (const el of sequenceElements) {
7536
- translSeqCodonStartGenomicPosArr.push({
7537
- codonGenomicPos: el.key,
7538
- sequenceLetter: el.props.children,
7539
- });
7540
- }
7541
- if (translSeqCodonStartGenomicPosArr.length === 0) {
7542
- return;
7543
- }
7544
- // Trim any sequence before first start codon and after stop codon
7545
- const startCodonIndex = translationSequence.indexOf('M');
7546
- const stopCodonIndex = translationSequence.indexOf('*');
7547
- const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos;
7548
- const stopCodonPos = translSeqCodonStartGenomicPosArr[stopCodonIndex].codonGenomicPos;
7549
- if (!startCodonPos || !stopCodonPos) {
7550
- return;
7551
- }
7552
- const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
7553
- let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
7554
- if (strand === 1) {
7555
- if (startCodonGenomicLoc > stopCodonGenomicLoc) {
7556
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
7557
- return;
7558
- }
7559
- let promise;
7560
- stopCodonGenomicLoc += 3; // move to end of stop codon
7561
- if (startCodonGenomicLoc !== cdsMin) {
7562
- promise = new Promise((resolve) => {
7563
- updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
7564
- resolve(true);
7565
- });
7566
- });
7567
- }
7568
- if (stopCodonGenomicLoc !== cdsMax) {
7569
- if (promise) {
7570
- void promise.then(() => {
7571
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
7572
- });
7573
- }
7574
- else {
7575
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
7576
- }
7577
- }
7578
- }
7579
- if (strand === -1) {
7580
- // reverse strand
7581
- if (startCodonGenomicLoc < stopCodonGenomicLoc) {
7582
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
7583
- return;
7584
- }
7585
- let promise;
7586
- stopCodonGenomicLoc -= 3; // move to end of stop codon
7587
- if (startCodonGenomicLoc !== cdsMax) {
7588
- promise = new Promise((resolve) => {
7589
- updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
7590
- resolve(true);
7591
- });
7592
- });
7593
- }
7594
- if (stopCodonGenomicLoc !== cdsMin) {
7595
- if (promise) {
7596
- void promise.then(() => {
7597
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
7598
- });
7599
- }
7600
- else {
7601
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
7602
- }
7603
- }
7604
- }
7605
- notify('Translation sequence trimmed to start and stop codons', 'success');
7606
- };
7607
- const onCopyClick = () => {
7608
- const seqDiv = seqRef.current;
7609
- if (!seqDiv) {
7610
- return;
7611
- }
7612
- void copyToClipboard(seqDiv);
7613
- };
7614
7814
  return (jsxRuntime.jsxs("div", { children: [cdsPresent && (jsxRuntime.jsx("div", { children: jsxRuntime.jsxs(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center' }, children: [jsxRuntime.jsx(material.Grid, { size: 1 }), strand === 1 ? (jsxRuntime.jsx(material.Grid, { size: 4, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
7615
7815
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
7616
7816
  }, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMax, onChangeCommitted: (newLocation) => {
@@ -7620,8 +7820,12 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7620
7820
  }, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: cdsMin + 1, onChangeCommitted: (newLocation) => {
7621
7821
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
7622
7822
  }, style: { border: '1px solid black', borderRadius: 5 }, disabled: changeInProgress }) })), jsxRuntime.jsx(material.Grid, { size: 1 })] }) })), jsxRuntime.jsxs("div", { style: { marginTop: 5, marginBottom: 10 }, children: [jsxRuntime.jsx("div", { style: { textAlign: 'center' }, children: jsxRuntime.jsx(material.Typography, { children: "Exons" }) }), transcriptExonParts.map((loc, index) => {
7623
- return (jsxRuntime.jsx("div", { children: loc.type === 'exon' && (jsxRuntime.jsxs(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center' }, children: [jsxRuntime.jsx(material.Grid, { size: 1, children: index !== 0 &&
7624
- getFivePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography, { component: 'span', color: site.color, children: site.spliceSite }, idx))) }), strand === 1 ? (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
7823
+ return (
7824
+ // eslint-disable-next-line @eslint-react/no-array-index-key
7825
+ jsxRuntime.jsx("div", { children: loc.type === 'exon' && (jsxRuntime.jsxs(material.Grid, { container: true, justifyContent: "center", alignItems: "center", style: { textAlign: 'center' }, children: [jsxRuntime.jsx(material.Grid, { size: 1, children: index !== 0 &&
7826
+ getFivePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
7827
+ // eslint-disable-next-line @eslint-react/no-array-index-key
7828
+ , { component: 'span', color: site.color, children: site.spliceSite }, idx))) }), strand === 1 ? (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
7625
7829
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
7626
7830
  }, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.max, onChangeCommitted: (newLocation) => {
7627
7831
  return handleExonLocationChange(loc.max, newLocation, feature, false);
@@ -7630,14 +7834,10 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7630
7834
  }, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.min + 1, onChangeCommitted: (newLocation) => {
7631
7835
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
7632
7836
  }, disabled: changeInProgress }) })), jsxRuntime.jsx(material.Grid, { size: 1, children: index !== transcriptExonParts.length - 1 &&
7633
- getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography, { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
7634
- })] }), cdsPresent && (jsxRuntime.jsx("div", { children: jsxRuntime.jsxs(material.Accordion, { children: [jsxRuntime.jsx(StyledAccordionSummary$1, { expandIcon: jsxRuntime.jsx(ExpandMoreIcon, { style: { color: 'white' } }), "aria-controls": "panel1-content", id: "panel1-header", children: jsxRuntime.jsx(material.Typography, { component: "span", fontWeight: 'bold', children: "Translation" }) }), jsxRuntime.jsxs(material.AccordionDetails, { children: [jsxRuntime.jsx(SequenceContainer, { children: jsxRuntime.jsx(material.Typography, { component: 'span', ref: seqRef, style: { maxHeight: 120, overflowY: 'scroll' }, children: getTranslationSequence() }) }), jsxRuntime.jsxs("div", { style: {
7635
- marginTop: 10,
7636
- display: 'flex',
7637
- flexDirection: 'row',
7638
- alignItems: 'center',
7639
- gap: 10,
7640
- }, children: [jsxRuntime.jsx(material.Tooltip, { title: "Copy", children: jsxRuntime.jsx("button", { onClick: onCopyClick, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCopyIcon, { style: { fontSize: 15 } }) }) }), jsxRuntime.jsx(material.Tooltip, { title: "Trim", children: jsxRuntime.jsx("button", { onClick: trimTranslationSequence, style: { border: 'none', background: 'none', padding: 0 }, disabled: changeInProgress, children: jsxRuntime.jsx(ContentCutIcon, { style: { fontSize: 15 } }) }) })] })] })] }) }))] }));
7837
+ getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
7838
+ // eslint-disable-next-line @eslint-react/no-array-index-key
7839
+ , { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
7840
+ })] }), cdsPresent && (jsxRuntime.jsx(Translation, { changeInProgress: changeInProgress, cdsLocations: cdsLocations, refData: refData, strand: strand, updateCDSLocation: updateCDSLocation, cdsMin: cdsMin, cdsMax: cdsMax, feature: feature, session: session }))] }));
7641
7841
  });
7642
7842
 
7643
7843
  const HeaderTableCell = styled(material.TableCell)(() => ({
@@ -7674,7 +7874,7 @@ const ApolloTranscriptDetailsWidget = mobxReact.observer(function ApolloTranscri
7674
7874
  const { assembly, feature, refName } = model;
7675
7875
  React.useEffect(() => {
7676
7876
  setPanelState(DEFAULT_PANELS);
7677
- // eslint-disable-next-line react-hooks/exhaustive-deps
7877
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
7678
7878
  }, [feature]);
7679
7879
  const session = util.getSession(model);
7680
7880
  const { pluginManager } = util.getEnv(session);
@@ -7733,8 +7933,8 @@ function handleFeatureTypeChange(changeManager, feature, oldType, newType) {
7733
7933
  typeName: 'TypeChange',
7734
7934
  changedIds: [featureId],
7735
7935
  featureId,
7736
- oldType: String(oldType),
7737
- newType: String(newType),
7936
+ oldType,
7937
+ newType,
7738
7938
  assembly: feature.assemblyId,
7739
7939
  });
7740
7940
  return changeManager.submit(change);
@@ -7830,7 +8030,7 @@ const NumberCell = mobxReact.observer(function NumberCell({ initialValue, notify
7830
8030
  if (initialValue !== value) {
7831
8031
  setValue(initialValue);
7832
8032
  }
7833
- // eslint-disable-next-line react-hooks/exhaustive-deps
8033
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
7834
8034
  }, [initialValue]);
7835
8035
  React.useEffect(() => {
7836
8036
  if (blur) {
@@ -8671,8 +8871,9 @@ function draw$3(display, ctx, gene, row, rowInFeature, block) {
8671
8871
  const [top, left, width] = getFeatureBox(display, gene, row, block);
8672
8872
  const height = getRowCount$2(display, gene) * apolloRowHeight;
8673
8873
  if (width > 2) {
8674
- let selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', { featureType: gene.type });
8675
- selectedColor = material.alpha(theme.palette.background.paper, 0.6);
8874
+ const selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', {
8875
+ featureType: gene.type,
8876
+ }) ?? material.alpha(theme.palette.background.paper, 0.6);
8676
8877
  ctx.fillStyle = selectedColor;
8677
8878
  ctx.fillRect(left, top, width, height);
8678
8879
  }
@@ -8945,7 +9146,7 @@ function getLayout(display, feature) {
8945
9146
  }
8946
9147
  return layout;
8947
9148
  }
8948
- function getContextMenuItems$1(display, feature) {
9149
+ function getContextMenuItems$1(display, transcript) {
8949
9150
  const { changeManager, regions, selectedFeature, session } = display;
8950
9151
  const [region] = regions;
8951
9152
  const currentAssemblyId = display.getAssemblyId(region.assemblyName);
@@ -8955,7 +9156,7 @@ function getContextMenuItems$1(display, feature) {
8955
9156
  label: 'Open transcript editor',
8956
9157
  onClick: () => {
8957
9158
  const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
8958
- feature,
9159
+ feature: transcript,
8959
9160
  assembly: currentAssemblyId,
8960
9161
  changeManager,
8961
9162
  refName: region.refName,
@@ -8975,7 +9176,7 @@ function getContextMenuItems$1(display, feature) {
8975
9176
  doneCallback();
8976
9177
  },
8977
9178
  changeManager,
8978
- sourceFeature: feature,
9179
+ sourceFeature: transcript,
8979
9180
  sourceAssemblyId: currentAssemblyId,
8980
9181
  selectedFeature,
8981
9182
  setSelectedFeature: (feature) => {
@@ -8995,7 +9196,7 @@ function getContextMenuItems$1(display, feature) {
8995
9196
  doneCallback();
8996
9197
  },
8997
9198
  changeManager,
8998
- sourceFeature: feature,
9199
+ sourceFeature: transcript,
8999
9200
  sourceAssemblyId: currentAssemblyId,
9000
9201
  selectedFeature,
9001
9202
  setSelectedFeature: (feature) => {
@@ -9005,6 +9206,31 @@ function getContextMenuItems$1(display, feature) {
9005
9206
  ]);
9006
9207
  },
9007
9208
  });
9209
+ const { children } = transcript;
9210
+ if (!children) {
9211
+ return [];
9212
+ }
9213
+ const cdsChildren = [...children.values()].filter((child) => isCDSFeature(child, session));
9214
+ if (cdsChildren.length === 0) {
9215
+ menuItems.push({
9216
+ label: 'Add coding sequence',
9217
+ onClick: () => {
9218
+ session.queueDialog((doneCallback) => [
9219
+ AddCodingSequence,
9220
+ {
9221
+ session,
9222
+ handleClose: () => {
9223
+ doneCallback();
9224
+ },
9225
+ changeManager,
9226
+ sourceFeature: transcript,
9227
+ sourceAssemblyId: currentAssemblyId,
9228
+ refName: region.refName,
9229
+ },
9230
+ ]);
9231
+ },
9232
+ });
9233
+ }
9008
9234
  return menuItems;
9009
9235
  }
9010
9236
  // False positive here, none of these functions use "this"
@@ -9182,7 +9408,7 @@ function colorCode(letter, theme) {
9182
9408
  letterUpper === 'C' ||
9183
9409
  letterUpper === 'G' ||
9184
9410
  letterUpper === 'T') {
9185
- return theme.palette.bases[letterUpper].main.toString();
9411
+ return theme.palette.bases[letterUpper].main;
9186
9412
  }
9187
9413
  return 'lightgray';
9188
9414
  }
@@ -9279,8 +9505,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9279
9505
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
9280
9506
  },
9281
9507
  get changeManager() {
9282
- return self.session.apolloDataStore
9283
- .changeManager;
9508
+ return self.session.apolloDataStore.changeManager;
9284
9509
  },
9285
9510
  getAssemblyId(assemblyName) {
9286
9511
  const { assemblyManager } = self.session;
@@ -9291,12 +9516,10 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9291
9516
  return assembly.name;
9292
9517
  },
9293
9518
  get selectedFeature() {
9294
- return self.session
9295
- .apolloSelectedFeature;
9519
+ return self.session.apolloSelectedFeature;
9296
9520
  },
9297
9521
  get hoveredFeature() {
9298
- return self.session
9299
- .apolloHoveredFeature;
9522
+ return self.session.apolloHoveredFeature;
9300
9523
  },
9301
9524
  }))
9302
9525
  .actions((self) => ({
@@ -9341,8 +9564,11 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9341
9564
  const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
9342
9565
  return {
9343
9566
  trackMenuItems() {
9344
- const { graphical, table, showCheckResults } = self;
9345
- return [
9567
+ const { graphical, table, showCheckResults, session } = self;
9568
+ const [region] = self.regions;
9569
+ const { assemblyName } = region;
9570
+ const backendDriver = session.apolloDataStore.getBackendDriver(assemblyName);
9571
+ const items = [
9346
9572
  ...superTrackMenuItems(),
9347
9573
  {
9348
9574
  type: 'subMenu',
@@ -9394,7 +9620,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9394
9620
  {
9395
9621
  label: 'Filter features by type',
9396
9622
  onClick: () => {
9397
- const session = self.session;
9623
+ const { session } = self;
9398
9624
  self.session.queueDialog((doneCallback) => [
9399
9625
  FilterFeatures,
9400
9626
  {
@@ -9410,76 +9636,93 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9410
9636
  ]);
9411
9637
  },
9412
9638
  },
9413
- {
9414
- label: 'Export annotations',
9415
- icon: Export,
9639
+ ];
9640
+ if (backendDriver instanceof LocalDriver) {
9641
+ items.push({
9642
+ label: 'Import annotations',
9643
+ icon: InputIcon,
9416
9644
  onClick: () => {
9417
- const [region] = self.regions;
9418
- const { assemblyName } = region;
9419
- const assembly = self.getAssemblyId(assemblyName);
9420
- if (!assembly) {
9421
- return;
9422
- }
9423
- const session = self.session;
9424
9645
  session.queueDialog((doneCallback) => [
9425
- DownloadGFF3,
9646
+ ImportAnnotations,
9426
9647
  {
9427
9648
  session,
9649
+ assemblyName,
9428
9650
  handleClose: () => {
9429
9651
  doneCallback();
9430
9652
  },
9431
- assembly,
9432
9653
  },
9433
9654
  ]);
9434
9655
  },
9656
+ });
9657
+ }
9658
+ items.push({
9659
+ label: 'Export annotations',
9660
+ icon: Export,
9661
+ onClick: () => {
9662
+ const [region] = self.regions;
9663
+ const { assemblyName } = region;
9664
+ const assembly = self.getAssemblyId(assemblyName);
9665
+ if (!assembly) {
9666
+ return;
9667
+ }
9668
+ const { session } = self;
9669
+ session.queueDialog((doneCallback) => [
9670
+ DownloadGFF3,
9671
+ {
9672
+ session,
9673
+ handleClose: () => {
9674
+ doneCallback();
9675
+ },
9676
+ assembly,
9677
+ },
9678
+ ]);
9435
9679
  },
9436
- {
9437
- label: 'View Change Log',
9438
- icon: TrackChangesIcon,
9439
- onClick: () => {
9440
- const [region] = self.regions;
9441
- const { assemblyName } = region;
9442
- const assembly = self.getAssemblyId(assemblyName);
9443
- if (!assembly) {
9444
- return;
9445
- }
9446
- const session = self.session;
9447
- session.queueDialog((doneCallback) => [
9448
- ViewChangeLog,
9449
- {
9450
- session,
9451
- handleClose: () => {
9452
- doneCallback();
9453
- },
9454
- assembly,
9680
+ }, {
9681
+ label: 'View Change Log',
9682
+ icon: TrackChangesIcon,
9683
+ onClick: () => {
9684
+ const [region] = self.regions;
9685
+ const { assemblyName } = region;
9686
+ const assembly = self.getAssemblyId(assemblyName);
9687
+ if (!assembly) {
9688
+ return;
9689
+ }
9690
+ const { session } = self;
9691
+ session.queueDialog((doneCallback) => [
9692
+ ViewChangeLog,
9693
+ {
9694
+ session,
9695
+ handleClose: () => {
9696
+ doneCallback();
9455
9697
  },
9456
- ]);
9457
- },
9698
+ assembly,
9699
+ },
9700
+ ]);
9458
9701
  },
9459
- {
9460
- label: 'View Check Results',
9461
- icon: FactCheckIcon,
9462
- onClick: () => {
9463
- const [region] = self.regions;
9464
- const { assemblyName } = region;
9465
- const assembly = self.getAssemblyId(assemblyName);
9466
- if (!assembly) {
9467
- return;
9468
- }
9469
- const session = self.session;
9470
- session.queueDialog((doneCallback) => [
9471
- ViewCheckResults,
9472
- {
9473
- session,
9474
- handleClose: () => {
9475
- doneCallback();
9476
- },
9477
- assembly,
9702
+ }, {
9703
+ label: 'View Check Results',
9704
+ icon: FactCheckIcon,
9705
+ onClick: () => {
9706
+ const [region] = self.regions;
9707
+ const { assemblyName } = region;
9708
+ const assembly = self.getAssemblyId(assemblyName);
9709
+ if (!assembly) {
9710
+ return;
9711
+ }
9712
+ const { session } = self;
9713
+ session.queueDialog((doneCallback) => [
9714
+ ViewCheckResults,
9715
+ {
9716
+ session,
9717
+ handleClose: () => {
9718
+ doneCallback();
9478
9719
  },
9479
- ]);
9480
- },
9720
+ assembly,
9721
+ },
9722
+ ]);
9481
9723
  },
9482
- ];
9724
+ });
9725
+ return items;
9483
9726
  },
9484
9727
  };
9485
9728
  })
@@ -10545,8 +10788,7 @@ function baseModelFactory$1(_pluginManager, configSchema) {
10545
10788
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
10546
10789
  },
10547
10790
  get changeManager() {
10548
- return self.session.apolloDataStore
10549
- .changeManager;
10791
+ return self.session.apolloDataStore.changeManager;
10550
10792
  },
10551
10793
  getAssemblyId(assemblyName) {
10552
10794
  const { assemblyManager } = self.session;
@@ -10557,12 +10799,10 @@ function baseModelFactory$1(_pluginManager, configSchema) {
10557
10799
  return assembly.name;
10558
10800
  },
10559
10801
  get selectedFeature() {
10560
- return self.session
10561
- .apolloSelectedFeature;
10802
+ return self.session.apolloSelectedFeature;
10562
10803
  },
10563
10804
  get hoveredFeature() {
10564
- return self.session
10565
- .apolloHoveredFeature;
10805
+ return self.session.apolloHoveredFeature;
10566
10806
  },
10567
10807
  get height() {
10568
10808
  const { sequenceRowHeight } = self;
@@ -10774,11 +11014,9 @@ const FilterTranscripts = mobxReact.observer(function FilterTranscripts({ source
10774
11014
  onUpdate(newForms);
10775
11015
  setExcludedTranscripts(newForms);
10776
11016
  };
10777
- return (jsxRuntime.jsx(Dialog, { open: true, maxWidth: false, "data-testid": "filter-transcripts-dialog", title: "Filter transcripts by ID", handleClose: handleClose, children: jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select the alternate transcripts you want to display in the apollo track" }), jsxRuntime.jsx(material.Grid, { container: true, spacing: 2, children: jsxRuntime.jsx(material.Grid, { size: 8, children: jsxRuntime.jsx(material.FormGroup, { children: allTranscripts.map((item) => (
10778
- // eslint-disable-next-line react/jsx-key
10779
- jsxRuntime.jsx(material.FormControlLabel, { control: jsxRuntime.jsx(material.Checkbox, { checked: !excludedTranscripts.includes(item), onChange: () => {
11017
+ return (jsxRuntime.jsx(Dialog, { open: true, maxWidth: false, "data-testid": "filter-transcripts-dialog", title: "Filter transcripts by ID", handleClose: handleClose, children: jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsx(material.DialogContentText, { children: "Select the alternate transcripts you want to display in the apollo track" }), jsxRuntime.jsx(material.Grid, { container: true, spacing: 2, children: jsxRuntime.jsx(material.Grid, { size: 8, children: jsxRuntime.jsx(material.FormGroup, { children: allTranscripts.map((item) => (jsxRuntime.jsx(material.FormControlLabel, { control: jsxRuntime.jsx(material.Checkbox, { checked: !excludedTranscripts.includes(item), onChange: () => {
10780
11018
  handleChange(item);
10781
- }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }))) }) }) })] }) }));
11019
+ }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }, item))) }) }) })] }) }));
10782
11020
  });
10783
11021
 
10784
11022
  let forwardFillLight = null;
@@ -11624,8 +11862,7 @@ function baseModelFactory(_pluginManager, configSchema) {
11624
11862
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
11625
11863
  },
11626
11864
  get changeManager() {
11627
- return self.session.apolloDataStore
11628
- .changeManager;
11865
+ return self.session.apolloDataStore.changeManager;
11629
11866
  },
11630
11867
  getAssemblyId(assemblyName) {
11631
11868
  const { assemblyManager } = self.session;
@@ -11636,12 +11873,10 @@ function baseModelFactory(_pluginManager, configSchema) {
11636
11873
  return assembly.name;
11637
11874
  },
11638
11875
  get selectedFeature() {
11639
- return self.session
11640
- .apolloSelectedFeature;
11876
+ return self.session.apolloSelectedFeature;
11641
11877
  },
11642
11878
  get hoveredFeature() {
11643
- return self.session
11644
- .apolloHoveredFeature;
11879
+ return self.session.apolloHoveredFeature;
11645
11880
  },
11646
11881
  }))
11647
11882
  .actions((self) => ({
@@ -11769,7 +12004,7 @@ function baseModelFactory(_pluginManager, configSchema) {
11769
12004
  {
11770
12005
  label: 'Filter features by type',
11771
12006
  onClick: () => {
11772
- const session = self.session;
12007
+ const { session } = self;
11773
12008
  self.session.queueDialog((doneCallback) => [
11774
12009
  FilterFeatures,
11775
12010
  {
@@ -12207,17 +12442,12 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
12207
12442
  if (!featureTypeOntology) {
12208
12443
  throw new Error('featureTypeOntology is undefined');
12209
12444
  }
12210
- let foundFeature;
12211
- if (self.geneTrackRowNums.includes(row)) {
12212
- foundFeature = layoutRow.find((f) => f.feature.type == 'exon' &&
12445
+ const foundFeature = self.geneTrackRowNums.includes(row)
12446
+ ? layoutRow.find((f) => f.feature.type == 'exon' &&
12213
12447
  bp >= f.feature.min &&
12214
- bp <= f.feature.max);
12215
- if (!foundFeature) {
12216
- foundFeature = layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max);
12217
- }
12218
- }
12219
- else {
12220
- foundFeature = layoutRow.find((f) => {
12448
+ bp <= f.feature.max) ??
12449
+ layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max)
12450
+ : layoutRow.find((f) => {
12221
12451
  const { feature } = f;
12222
12452
  const featureID = feature.attributes.get('gff_id')?.toString();
12223
12453
  const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript');
@@ -12239,7 +12469,6 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
12239
12469
  }
12240
12470
  return false;
12241
12471
  });
12242
- }
12243
12472
  if (!foundFeature) {
12244
12473
  return mousePosition;
12245
12474
  }
@@ -12563,26 +12792,32 @@ function CreateApolloAnnotation({ annotationFeature, assembly, handleClose, refS
12563
12792
  const apolloAssembly = apolloSessionModel.apolloDataStore.assemblies.get(assembly.name);
12564
12793
  const refSeq = apolloAssembly?.refSeqs.get(refSeqId);
12565
12794
  const features = refSeq?.getFeatures(region.start, region.end);
12566
- const getDestinationFeatures = () => {
12567
- const filteredFeatures = [];
12568
- for (const f of features ?? []) {
12569
- if (f.min > region.end || f.max < region.start) {
12570
- continue;
12571
- }
12572
- // Destination feature should be of type gene
12573
- if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
12574
- const featureSnapshot = mobxStateTree.getSnapshot(f);
12575
- filteredFeatures.push(featureSnapshot);
12576
- }
12577
- }
12578
- return filteredFeatures;
12579
- };
12580
12795
  React.useEffect(() => {
12796
+ const getDestinationFeatures = () => {
12797
+ const filteredFeatures = [];
12798
+ for (const f of features ?? []) {
12799
+ if (f.min > region.end || f.max < region.start) {
12800
+ continue;
12801
+ }
12802
+ // Destination feature should be of type gene
12803
+ if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
12804
+ const featureSnapshot = mobxStateTree.getSnapshot(f);
12805
+ filteredFeatures.push(featureSnapshot);
12806
+ }
12807
+ }
12808
+ return filteredFeatures;
12809
+ };
12581
12810
  setErrorMessage('');
12582
- const features = getDestinationFeatures();
12583
- setDestinationFeatures(features);
12584
- setSelectedDestinationFeature(features[0]);
12585
- }, [checkedChildrens, parentFeatureChecked, region]);
12811
+ const destinationFeatures = getDestinationFeatures();
12812
+ setDestinationFeatures(destinationFeatures);
12813
+ setSelectedDestinationFeature(destinationFeatures[0]);
12814
+ }, [
12815
+ checkedChildrens,
12816
+ featureTypeOntology,
12817
+ features,
12818
+ parentFeatureChecked,
12819
+ region,
12820
+ ]);
12586
12821
  const handleParentFeatureCheck = (event) => {
12587
12822
  const isChecked = event.target.checked;
12588
12823
  setParentFeatureChecked(isChecked);
@@ -12862,8 +13097,21 @@ function annotationFromPileup(pluggableElement) {
12862
13097
  getAnnotationFeature(jbrowseFeature, refSeqId) {
12863
13098
  const start = jbrowseFeature.get('start');
12864
13099
  const end = jbrowseFeature.get('end');
12865
- const strand = jbrowseFeature.get('strand');
12866
13100
  const name = jbrowseFeature.get('name');
13101
+ let strand;
13102
+ const tags = jbrowseFeature.get('tags');
13103
+ const { ts, TS, XS } = tags;
13104
+ for (const tag of [ts, TS, XS]) {
13105
+ if (!strand && tag) {
13106
+ if ([1, '1', '+'].includes(tag)) {
13107
+ strand = 1;
13108
+ }
13109
+ else if ([-1, '-1', '-'].includes(tag)) {
13110
+ strand = -1;
13111
+ }
13112
+ }
13113
+ }
13114
+ strand ??= jbrowseFeature.get('strand');
12867
13115
  const cigarData = jbrowseFeature.get('CIGAR');
12868
13116
  const ops = parseCigar(cigarData);
12869
13117
  let position = start;
@@ -12879,9 +13127,7 @@ function annotationFromPileup(pluggableElement) {
12879
13127
  case 'M':
12880
13128
  case '=':
12881
13129
  case 'X': {
12882
- if (currentExonStart === undefined) {
12883
- currentExonStart = position;
12884
- }
13130
+ currentExonStart ??= position;
12885
13131
  position += len;
12886
13132
  break;
12887
13133
  }
@@ -13295,7 +13541,7 @@ const LinearApolloDisplay = mobxReact.observer(function LinearApolloDisplay(prop
13295
13541
  : undefined, menuItems: contextMenuItems })] }))] }));
13296
13542
  });
13297
13543
 
13298
- const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx = 0, }) {
13544
+ const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx, }) {
13299
13545
  const { apolloRowHeight, highestRow, showFeatureLabels } = model;
13300
13546
  const featureLabelSpacer = showFeatureLabels ? 2 : 1;
13301
13547
  return (jsxRuntime.jsx("div", { style: {
@@ -13367,8 +13613,7 @@ const LinearApolloSixFrameDisplay = mobxReact.observer(function LinearApolloSixF
13367
13613
  regionNumber: idx,
13368
13614
  })?.offsetPx ?? 0) - lgv.offsetPx;
13369
13615
  const [feature] = checkResult.featureIds;
13370
- if (!feature ||
13371
- !feature.parent ||
13616
+ if (!feature?.parent ||
13372
13617
  !looksLikeGene(feature.parent, session)) {
13373
13618
  return null;
13374
13619
  }
@@ -13620,18 +13865,15 @@ function addTopLevelMenus(rootModel) {
13620
13865
  }
13621
13866
  }
13622
13867
 
13623
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
13624
13868
  /* eslint-disable @typescript-eslint/no-confusing-void-expression */
13625
13869
  const ApolloJobModel = mobxStateTree.types
13626
13870
  .model('JobsManager', {})
13627
13871
  .views((self) => ({
13628
13872
  get jobStatusWidget() {
13629
13873
  const { widgets } = util.getSession(self);
13630
- let jobStatusWidget = widgets.get('JobsList');
13631
- if (!jobStatusWidget) {
13874
+ const jobStatusWidget = widgets.get('JobsList') ??
13632
13875
  // @ts-expect-error: addWidget function not detected on the session
13633
- jobStatusWidget = util.getSession(self).addWidget('JobsListWidget', 'JobsList');
13634
- }
13876
+ util.getSession(self).addWidget('JobsListWidget', 'JobsList');
13635
13877
  return jobStatusWidget;
13636
13878
  },
13637
13879
  }))
@@ -13910,19 +14152,15 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
13910
14152
  continue;
13911
14153
  }
13912
14154
  const { assemblyName, refName } = region;
13913
- let assembly = self.assemblies.get(assemblyName);
13914
- if (!assembly) {
13915
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
13916
- }
14155
+ const assembly = self.assemblies.get(assemblyName) ??
14156
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} });
13917
14157
  const [firstFeature] = features;
13918
- let ref = assembly.refSeqs.get(firstFeature.refSeq);
13919
- if (!ref) {
13920
- ref = assembly.refSeqs.put({
14158
+ const ref = assembly.refSeqs.get(firstFeature.refSeq) ??
14159
+ assembly.refSeqs.put({
13921
14160
  _id: firstFeature.refSeq,
13922
14161
  name: refName,
13923
14162
  features: {},
13924
14163
  });
13925
- }
13926
14164
  for (const feature of features) {
13927
14165
  if (!ref.features.has(feature._id)) {
13928
14166
  ref.features.put(feature);
@@ -13939,24 +14177,153 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
13939
14177
  }
13940
14178
  const { refSeq, seq } = yield backendDriver.getSequence(region);
13941
14179
  const { assemblyName, end, refName, start } = region;
13942
- let assembly = self.assemblies.get(assemblyName);
13943
- if (!assembly) {
13944
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
13945
- }
13946
- let ref = assembly.refSeqs.get(refSeq);
13947
- if (!ref) {
13948
- ref = assembly.refSeqs.put({
13949
- _id: refSeq,
13950
- name: refName,
13951
- sequence: [],
13952
- });
13953
- }
14180
+ const assembly = self.assemblies.get(assemblyName) ??
14181
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} });
14182
+ const ref = assembly.refSeqs.get(refSeq) ??
14183
+ assembly.refSeqs.put({ _id: refSeq, name: refName, sequence: [] });
13954
14184
  ref.addSequence({ start, stop: end, sequence: seq });
13955
14185
  }
13956
14186
  }),
13957
14187
  }));
13958
14188
  }
13959
14189
 
14190
+ function reassignIds(feature) {
14191
+ const newChildren = {};
14192
+ if (feature.children) {
14193
+ for (const child of Object.values(feature.children)) {
14194
+ const newChild = reassignIds(child);
14195
+ newChildren[newChild._id] = newChild;
14196
+ }
14197
+ }
14198
+ return {
14199
+ ...feature,
14200
+ _id: new ObjectID().toHexString(),
14201
+ children: feature.children ? newChildren : undefined,
14202
+ };
14203
+ }
14204
+ function DuplicateFeatureDialog({ assemblyName, changeManager, existingFeature, featureSnapshot, handleClose, }) {
14205
+ const [choice, setChoice] = React.useState('keepExisting');
14206
+ const [errorMessage, setErrorMessage] = React.useState('');
14207
+ const [submitted, setSubmitted] = React.useState(false);
14208
+ const handleSubmit = async () => {
14209
+ setErrorMessage('');
14210
+ setSubmitted(true);
14211
+ try {
14212
+ if (choice === 'useNew') {
14213
+ const deleteChange = new shared.DeleteFeatureChange({
14214
+ typeName: 'DeleteFeatureChange',
14215
+ assembly: assemblyName,
14216
+ changedIds: [existingFeature._id],
14217
+ deletedFeature: existingFeature,
14218
+ });
14219
+ await changeManager.submit(deleteChange);
14220
+ const addChange = new shared.AddFeatureChange({
14221
+ typeName: 'AddFeatureChange',
14222
+ assembly: assemblyName,
14223
+ changedIds: [featureSnapshot._id],
14224
+ addedFeature: featureSnapshot,
14225
+ });
14226
+ await changeManager.submit(addChange);
14227
+ }
14228
+ else if (choice === 'keepBoth') {
14229
+ const newSnapshot = reassignIds(featureSnapshot);
14230
+ const addChange = new shared.AddFeatureChange({
14231
+ typeName: 'AddFeatureChange',
14232
+ assembly: assemblyName,
14233
+ changedIds: [newSnapshot._id],
14234
+ addedFeature: newSnapshot,
14235
+ });
14236
+ await changeManager.submit(addChange);
14237
+ }
14238
+ handleClose();
14239
+ }
14240
+ catch (error) {
14241
+ setErrorMessage(String(error));
14242
+ setSubmitted(false);
14243
+ }
14244
+ };
14245
+ return (jsxRuntime.jsxs(Dialog, { open: true, title: "Duplicate Feature Detected", handleClose: handleClose, maxWidth: "sm", fullWidth: true, children: [jsxRuntime.jsxs(material.DialogContent, { children: [jsxRuntime.jsxs(material.DialogContentText, { children: ["A feature with ID \u201C", featureSnapshot._id, "\u201D already exists. How would you like to resolve this conflict?"] }), jsxRuntime.jsxs(material.FormControl, { component: "fieldset", sx: { mt: 2 }, children: [jsxRuntime.jsx(material.FormLabel, { component: "legend", children: "Resolution" }), jsxRuntime.jsxs(material.RadioGroup, { value: choice, onChange: (e) => {
14246
+ setChoice(e.target.value);
14247
+ }, children: [jsxRuntime.jsx(material.FormControlLabel, { value: "keepExisting", control: jsxRuntime.jsx(material.Radio, {}), label: "Keep the existing feature" }), jsxRuntime.jsx(material.FormControlLabel, { value: "useNew", control: jsxRuntime.jsx(material.Radio, {}), label: "Replace with the new feature" }), jsxRuntime.jsx(material.FormControlLabel, { value: "keepBoth", control: jsxRuntime.jsx(material.Radio, {}), label: "Keep both (assign new IDs to the incoming feature)" })] })] }), errorMessage ? (jsxRuntime.jsx(material.DialogContentText, { color: "error", sx: { mt: 1 }, children: errorMessage })) : null] }), jsxRuntime.jsxs(material.DialogActions, { children: [jsxRuntime.jsx(material.Button, { variant: "contained", onClick: handleSubmit, disabled: submitted, children: "Confirm" }), jsxRuntime.jsx(material.Button, { onClick: handleClose, disabled: submitted, children: "Cancel" })] })] }));
14248
+ }
14249
+
14250
+ function fromUrlSafeBase64(urlSafeBase64) {
14251
+ let base64 = urlSafeBase64.replaceAll('-', '+').replaceAll('_', '/');
14252
+ const pad = base64.length % 4;
14253
+ if (pad) {
14254
+ base64 += '='.repeat(4 - pad);
14255
+ }
14256
+ return base64;
14257
+ }
14258
+ // Base64 decode + decompress
14259
+ async function decompress(encoded) {
14260
+ const binaryString = atob(encoded);
14261
+ // eslint-disable-next-line unicorn/prefer-code-point
14262
+ const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
14263
+ const stream = new Blob([bytes]).stream();
14264
+ const decompressionStream = new DecompressionStream('gzip');
14265
+ const decompressedStream = stream.pipeThrough(decompressionStream);
14266
+ const response = new Response(decompressedStream);
14267
+ const decompressed = await response.arrayBuffer();
14268
+ return JSON.parse(new TextDecoder().decode(decompressed));
14269
+ }
14270
+ async function handleApolloFeaturesUrlParam(encodedFeatures, apolloDataStore, assemblyManager, session) {
14271
+ const base64 = fromUrlSafeBase64(encodedFeatures);
14272
+ const decompressed = await decompress(base64);
14273
+ const featuresData = decompressed;
14274
+ for (const [assemblyName, featureSnapshots] of Object.entries(featuresData)) {
14275
+ const backendDriver = apolloDataStore.getBackendDriver(assemblyName);
14276
+ if (!(backendDriver instanceof LocalDriver)) {
14277
+ continue;
14278
+ }
14279
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
14280
+ if (!assembly) {
14281
+ throw new Error(`Assembly not found: "${assemblyName}"`);
14282
+ }
14283
+ const { regions } = assembly;
14284
+ if (!regions) {
14285
+ throw new Error(`Assembly not found: "${assemblyName}"`);
14286
+ }
14287
+ const refNames = regions.map((r) => r.refName);
14288
+ const db = await openDb(assemblyName, refNames);
14289
+ for (const featureSnapshot of featureSnapshots) {
14290
+ const storeName = `features-${featureSnapshot.refSeq}`;
14291
+ const existing = (await db.get(storeName, featureSnapshot._id));
14292
+ if (existing === undefined) {
14293
+ const change = new shared.AddFeatureChange({
14294
+ typeName: 'AddFeatureChange',
14295
+ assembly: assemblyName,
14296
+ changedIds: [featureSnapshot._id],
14297
+ addedFeature: featureSnapshot,
14298
+ });
14299
+ await apolloDataStore.changeManager.submit(change);
14300
+ }
14301
+ else {
14302
+ // get rid of undefined values in JSON
14303
+ // eslint-disable-next-line unicorn/prefer-structured-clone
14304
+ const existingFeature = JSON.parse(JSON.stringify(existing));
14305
+ if (!equal(featureSnapshot, existingFeature)) {
14306
+ await new Promise((resolve) => {
14307
+ session.queueDialog((doneCallback) => [
14308
+ DuplicateFeatureDialog,
14309
+ {
14310
+ featureSnapshot,
14311
+ existingFeature,
14312
+ assemblyName,
14313
+ changeManager: apolloDataStore.changeManager,
14314
+ handleClose: () => {
14315
+ doneCallback();
14316
+ resolve();
14317
+ },
14318
+ },
14319
+ ]);
14320
+ });
14321
+ }
14322
+ }
14323
+ }
14324
+ }
14325
+ }
14326
+
13960
14327
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
13961
14328
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
13962
14329
  function extendSession(pluginManager, sessionModel) {
@@ -14042,8 +14409,7 @@ function extendSession(pluginManager, sessionModel) {
14042
14409
  for (const block of dynamicBlocks.contentBlocks) {
14043
14410
  const { assemblyName, end, refName, start } = block;
14044
14411
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
14045
- if (assembly &&
14046
- assembly.backendDriverType === 'CollaborationServerDriver') {
14412
+ if (assembly?.backendDriverType === 'CollaborationServerDriver') {
14047
14413
  locations.push({ assemblyName, refName, start, end });
14048
14414
  }
14049
14415
  }
@@ -14102,8 +14468,8 @@ function extendSession(pluginManager, sessionModel) {
14102
14468
  for (const block of dynamicBlocks.contentBlocks) {
14103
14469
  const { assemblyName, end, refName, start } = block;
14104
14470
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
14105
- if (assembly &&
14106
- assembly.backendDriverType === 'CollaborationServerDriver') {
14471
+ if (assembly?.backendDriverType ===
14472
+ 'CollaborationServerDriver') {
14107
14473
  locations.push({ assemblyName, refName, start, end });
14108
14474
  }
14109
14475
  }
@@ -14223,6 +14589,37 @@ function extendSession(pluginManager, sessionModel) {
14223
14589
  beforeDestroy() {
14224
14590
  self.abortController.abort(new DOMException('Clean up Apollo session', 'AbortError'));
14225
14591
  },
14592
+ }))
14593
+ .actions((self) => ({
14594
+ async afterCreate() {
14595
+ if (util.isElectron) {
14596
+ return;
14597
+ }
14598
+ const url = new URL(globalThis.location.href);
14599
+ const apolloFeatures = url.searchParams.get('apolloFeatures');
14600
+ if (!apolloFeatures) {
14601
+ return;
14602
+ }
14603
+ const { assemblyManager } = self;
14604
+ await handleApolloFeaturesUrlParam(apolloFeatures, self.apolloDataStore, assemblyManager, self);
14605
+ await new Promise((resolve) => setTimeout(resolve, 2000));
14606
+ const updatedURL = new URL(globalThis.location.href);
14607
+ updatedURL.searchParams.delete('apolloFeatures');
14608
+ globalThis.history.replaceState(null, '', updatedURL.toString());
14609
+ },
14610
+ afterAttach() {
14611
+ mobxStateTree.addDisposer(self, mobx.autorun((reaction) => {
14612
+ const { focusedViewId, activeWidgets } = self;
14613
+ if (!(focusedViewId && activeWidgets)) {
14614
+ return;
14615
+ }
14616
+ const trackSelector = activeWidgets.get('hierarchicalTrackSelector');
14617
+ // @ts-expect-error Don't have type for track selector
14618
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
14619
+ trackSelector?.setView(focusedViewId);
14620
+ reaction.dispose();
14621
+ }));
14622
+ },
14226
14623
  }))
14227
14624
  .views((self) => {
14228
14625
  const superTrackActions = self
@@ -14354,7 +14751,6 @@ function extendSession(pluginManager, sessionModel) {
14354
14751
  }
14355
14752
 
14356
14753
  /* eslint-disable @typescript-eslint/unbound-method */
14357
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
14358
14754
  /* eslint-disable @typescript-eslint/no-misused-promises */
14359
14755
  /* eslint-disable @typescript-eslint/no-explicit-any */
14360
14756
  function isApolloMessageData(data) {
@@ -14542,10 +14938,10 @@ class ApolloPlugin extends Plugin {
14542
14938
  });
14543
14939
  if (!inWebWorker) {
14544
14940
  pluginManager.addToExtensionPoint('Core-extendWorker', (handle) => {
14545
- if (!('on' in handle && handle.on)) {
14941
+ if (!('on' in handle.client && handle.client.on)) {
14546
14942
  return handle;
14547
14943
  }
14548
- handle.on('apollo', async (event) => {
14944
+ handle.client.on('apollo', async (event) => {
14549
14945
  if (!isApolloMessageData(event)) {
14550
14946
  return;
14551
14947
  }
@@ -14563,7 +14959,7 @@ class ApolloPlugin extends Plugin {
14563
14959
  break;
14564
14960
  }
14565
14961
  const { seq: sequence } = await backendDriver.getSequence(region);
14566
- handle.workers[0].postMessage({
14962
+ handle.worker.postMessage({
14567
14963
  apollo,
14568
14964
  messageId,
14569
14965
  sequence,
@@ -14581,7 +14977,7 @@ class ApolloPlugin extends Plugin {
14581
14977
  break;
14582
14978
  }
14583
14979
  const regions = await backendDriver.getRegions(assembly);
14584
- handle.workers[0].postMessage({
14980
+ handle.worker.postMessage({
14585
14981
  apollo,
14586
14982
  messageId,
14587
14983
  regions,
@@ -14599,7 +14995,7 @@ class ApolloPlugin extends Plugin {
14599
14995
  break;
14600
14996
  }
14601
14997
  const refNameAliases = await backendDriver.getRefNameAliases(assembly);
14602
- handle.workers[0].postMessage({
14998
+ handle.worker.postMessage({
14603
14999
  apollo,
14604
15000
  messageId,
14605
15001
  refNameAliases,