@apollo-annotation/jbrowse-plugin-apollo 1.0.1 → 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 +826 -433
  89. package/dist/index.esm.js.map +1 -1
  90. package/dist/jbrowse-plugin-apollo.cjs.development.js +821 -428
  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 +859 -467
  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 +9 -3
  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.1";
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
  }
@@ -2885,7 +3030,7 @@ class LocalDriver extends BackendDriver {
2885
3030
  async getRegions(assemblyName) {
2886
3031
  const session = util.getSession(this.clientStore);
2887
3032
  const { assemblyManager } = session;
2888
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
3033
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
2889
3034
  if (!assembly) {
2890
3035
  throw new Error(`Assembly not found: "${assemblyName}"`);
2891
3036
  }
@@ -2901,7 +3046,7 @@ class LocalDriver extends BackendDriver {
2901
3046
  async getRefNameAliases(assemblyName) {
2902
3047
  const session = util.getSession(this.clientStore);
2903
3048
  const { assemblyManager } = session;
2904
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
3049
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
2905
3050
  if (!assembly) {
2906
3051
  throw new Error(`Assembly not found: "${assemblyName}"`);
2907
3052
  }
@@ -3560,7 +3705,7 @@ class CollaborationServerDriver extends BackendDriver {
3560
3705
  const localSessionId = shared.makeUserSessionId(token);
3561
3706
  const changeManager = new ChangeManager(this.clientStore);
3562
3707
  // Save server last change sequence into session storage
3563
- internetAccount.setLastChangeSequenceNumber(Number(message.changeSequence));
3708
+ internetAccount.setLastChangeSequenceNumber(message.changeSequence);
3564
3709
  if (message.userSessionId === localSessionId) {
3565
3710
  return; // we did this change, no need to apply it again
3566
3711
  }
@@ -3608,14 +3753,10 @@ class CollaborationServerDriver extends BackendDriver {
3608
3753
  const seq = await inFlightPromise;
3609
3754
  return { seq, refSeq };
3610
3755
  }
3611
- let apolloAssembly = this.clientStore.assemblies.get(assemblyName);
3612
- if (!apolloAssembly) {
3613
- apolloAssembly = this.clientStore.addAssembly(assemblyName);
3614
- }
3615
- let apolloRefSeq = apolloAssembly.refSeqs.get(refSeq);
3616
- if (!apolloRefSeq) {
3617
- apolloRefSeq = apolloAssembly.addRefSeq(refSeq, refName);
3618
- }
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);
3619
3760
  const clientStoreSequence = apolloRefSeq.getSequence(start, end);
3620
3761
  if (clientStoreSequence.length === end - start) {
3621
3762
  return { seq: clientStoreSequence, refSeq };
@@ -3972,7 +4113,7 @@ function AddFeature({ changeManager, handleClose, region, session, }) {
3972
4113
  setType(NewFeature[value]);
3973
4114
  }
3974
4115
  };
3975
- let submitDisabled = Boolean(error) || !(start && end && type);
4116
+ let submitDisabled = error || !(start && end && type);
3976
4117
  if ((type === NewFeature.CUSTOM && !customType) ||
3977
4118
  (!strand && type === NewFeature.GENE_AND_SUBFEATURES) ||
3978
4119
  (!strand && type === NewFeature.TRANSCRIPT_AND_SUBFEATURES)) {
@@ -3993,7 +4134,7 @@ function AddFeature({ changeManager, handleClose, region, session, }) {
3993
4134
  function DeleteAssembly({ changeManager, handleClose, session, }) {
3994
4135
  const { internetAccounts } = mobxStateTree.getRoot(session);
3995
4136
  const [errorMessage, setErrorMessage] = React.useState('');
3996
- const [confirmDelete, setconfirmDelete] = React.useState(false);
4137
+ const [confirmDelete, setConfirmDelete] = React.useState(false);
3997
4138
  const [submitted, setSubmitted] = React.useState(false);
3998
4139
  const apolloInternetAccounts = internetAccounts.filter((ia) => ia.type === 'ApolloInternetAccount');
3999
4140
  if (apolloInternetAccounts.length === 0) {
@@ -4034,7 +4175,7 @@ function DeleteAssembly({ changeManager, handleClose, session, }) {
4034
4175
  event.preventDefault();
4035
4176
  }
4036
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: () => {
4037
- setconfirmDelete(!confirmDelete);
4178
+ setConfirmDelete(!confirmDelete);
4038
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] }));
4039
4180
  }
4040
4181
 
@@ -4043,7 +4184,7 @@ function Export(props) {
4043
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" }) }));
4044
4185
  }
4045
4186
  function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
4046
- const [includeFASTA, setincludeFASTA] = React.useState(false);
4187
+ const [includeFASTA, setIncludeFASTA] = React.useState(false);
4047
4188
  const [errorMessage, setErrorMessage] = React.useState('');
4048
4189
  const { getInternetAccount } = session.apolloDataStore;
4049
4190
  const { assemblyManager } = session;
@@ -4092,7 +4233,7 @@ function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
4092
4233
  window.open(exportUri, '_blank');
4093
4234
  }
4094
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: () => {
4095
- setincludeFASTA(!includeFASTA);
4236
+ setIncludeFASTA(!includeFASTA);
4096
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] }));
4097
4238
  }
4098
4239
  function getAssemblyGFF3Stream(assemblyName) {
@@ -4131,6 +4272,49 @@ async function* getFeaturesForAssembly(assemblyName) {
4131
4272
  db.close();
4132
4273
  }
4133
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
+
4134
4318
  function ImportFeatures({ changeManager, handleClose, session, }) {
4135
4319
  const { apolloDataStore } = session;
4136
4320
  const [file, setFile] = React.useState();
@@ -4534,7 +4718,7 @@ function ManageUsers({ changeManager, handleClose, session, }) {
4534
4718
  });
4535
4719
  return newRow;
4536
4720
  }
4537
- 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) => {
4538
4722
  setErrorMessage(String(error));
4539
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] }));
4540
4724
  }
@@ -4844,12 +5028,13 @@ const AddRefSeqAliases = mobxReact.observer(function AddRefSeqAliases({ changeMa
4844
5028
  const [enableSubmit, setEnableSubmit] = React.useState(false);
4845
5029
  const [selectedAssembly, setSelectedAssembly] = React.useState();
4846
5030
  const [selectedRows, setSelectedRows] = React.useState([]);
4847
- const [refNameAliasMap, setRefNameAliasMap] = React.useState(new Map());
5031
+ const [refNameAliasMap, setRefNameAliasMap] = React.useState(() => new Map());
4848
5032
  const { apolloDataStore } = session;
4849
5033
  const { collaborationServerDriver } = apolloDataStore;
4850
5034
  const assemblies = collaborationServerDriver.getAssemblies();
4851
5035
  React.useEffect(() => {
4852
5036
  if (assemblies.length > 0) {
5037
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
4853
5038
  setSelectedAssembly(assemblies[0]);
4854
5039
  collaborationServerDriver
4855
5040
  .getRefNameAliases(assemblies[0].name)
@@ -4861,7 +5046,7 @@ const AddRefSeqAliases = mobxReact.observer(function AddRefSeqAliases({ changeMa
4861
5046
  setErrorMessage('Error fetching refName aliases for assembly');
4862
5047
  });
4863
5048
  }
4864
- // eslint-disable-next-line react-hooks/exhaustive-deps
5049
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
4865
5050
  }, []);
4866
5051
  const initializeRefNameAliasMap = (refNameAliasesList) => {
4867
5052
  const initialMap = new Map();
@@ -5266,17 +5451,23 @@ const useStyles$d = tssReact.makeStyles()((theme) => ({
5266
5451
  }));
5267
5452
  function GoogleButton(props) {
5268
5453
  const { classes } = useStyles$d();
5269
- const { disabled } = props;
5270
- 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 }));
5271
5456
  }
5272
5457
  function MicrosoftButton(props) {
5273
5458
  const { classes } = useStyles$d();
5274
- const { disabled } = props;
5275
- 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 }));
5276
5461
  }
5277
5462
  function GuestButton(props) {
5278
5463
  const { classes } = useStyles$d();
5279
- 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 }));
5280
5471
  }
5281
5472
 
5282
5473
  const useStyles$c = tssReact.makeStyles()((theme) => ({
@@ -5288,7 +5479,7 @@ const useStyles$c = tssReact.makeStyles()((theme) => ({
5288
5479
  const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
5289
5480
  const { classes } = useStyles$c();
5290
5481
  const [errorMessage, setErrorMessage] = React.useState('');
5291
- const [loginTypes, setLoginTypes] = React.useState([]);
5482
+ const [loginTypes, setLoginTypes] = React.useState(null);
5292
5483
  React.useEffect(() => {
5293
5484
  const controller = new AbortController();
5294
5485
  const { signal } = controller;
@@ -5302,6 +5493,9 @@ const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
5302
5493
  }
5303
5494
  const data = (await response.json());
5304
5495
  setLoginTypes(data);
5496
+ if (data.length === 0) {
5497
+ setErrorMessage('No login types configured');
5498
+ }
5305
5499
  }
5306
5500
  getAuthTypes().catch((error) => {
5307
5501
  if (!aborting.isAbortException(error)) {
@@ -5313,26 +5507,35 @@ const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
5313
5507
  };
5314
5508
  }, [baseURL]);
5315
5509
  function handleClick(authType) {
5316
- if (authType === 'google') {
5317
- handleClose('google');
5318
- }
5319
- else if (authType === 'microsoft') {
5320
- handleClose('microsoft');
5321
- }
5322
- else {
5323
- handleClose('guest');
5324
- }
5325
- }
5326
- const allowGoogle = loginTypes.includes('google');
5327
- const allowMicrosoft = loginTypes.includes('microsoft');
5328
- const allowGuest = loginTypes.includes('guest');
5329
- 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: () => {
5330
- handleClick('google');
5331
- } })) : null, allowMicrosoft ? (jsxRuntime.jsx(MicrosoftButton, { disabled: !allowMicrosoft, onClick: () => {
5332
- handleClick('microsoft');
5333
- } })) : null, allowGuest ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.Divider, { className: classes.divider }), jsxRuntime.jsx(GuestButton, { onClick: () => {
5334
- handleClick('guest');
5335
- } })] })) : 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: () => {
5336
5539
  handleClose();
5337
5540
  }, children: "Cancel" }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
5338
5541
  };
@@ -5528,13 +5731,13 @@ const stateModelFactory$3 = (configSchema) => {
5528
5731
  },
5529
5732
  ]);
5530
5733
  });
5531
- if (authType !== 'guest') {
5734
+ if (authType.needsPopup) {
5532
5735
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
5533
- self.openAuthWindow(authType, resolve, reject);
5736
+ self.openAuthWindow(authType.name, resolve, reject);
5534
5737
  return;
5535
5738
  }
5536
5739
  const url = new URL('auth/login', baseURL);
5537
- const searchParams = new URLSearchParams({ type: authType });
5740
+ const searchParams = new URLSearchParams({ type: authType.name });
5538
5741
  url.search = searchParams.toString();
5539
5742
  const uri = url.toString();
5540
5743
  const response = await fetch(uri, { signal: self.controller.signal });
@@ -5655,7 +5858,7 @@ const stateModelFactory$3 = (configSchema) => {
5655
5858
  socket.on('COMMON', (message) => {
5656
5859
  if ('checkResult' in message) {
5657
5860
  if (message.deleted) {
5658
- deleteCheckResult(message.checkResult._id.toString());
5861
+ deleteCheckResult(message.checkResult._id);
5659
5862
  }
5660
5863
  else {
5661
5864
  addCheckResult(message.checkResult);
@@ -6275,7 +6478,9 @@ const DefaultAttributeEditor = mobxReact.observer(function DefaultAttributeEdito
6275
6478
  return newValues;
6276
6479
  });
6277
6480
  }
6278
- 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) => {
6279
6484
  updateValue(idx, editedValue);
6280
6485
  }, variant: "outlined", fullWidth: true }), jsxRuntime.jsx(material.IconButton, { "aria-label": "delete", size: "medium", edge: "end", onClick: () => {
6281
6486
  deleteValue(idx);
@@ -6287,7 +6492,9 @@ const DefaultAttributeEditor = mobxReact.observer(function DefaultAttributeEdito
6287
6492
  });
6288
6493
 
6289
6494
  function DefaultAttributeViewer({ values }) {
6290
- 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}`))) }));
6291
6498
  }
6292
6499
 
6293
6500
  const useStyles$a = tssReact.makeStyles()((theme) => ({
@@ -6593,6 +6800,7 @@ const ApolloFeatureDetailsWidget = mobxReact.observer(function ApolloFeatureDeta
6593
6800
  const { classes } = useStyles$8();
6594
6801
  const [panelState, setPanelState] = React.useState(['attributes']);
6595
6802
  React.useEffect(() => {
6803
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
6596
6804
  setPanelState(['attributes']);
6597
6805
  }, [feature]);
6598
6806
  if (!(feature && currentAssembly)) {
@@ -6894,7 +7102,7 @@ const TranscriptSequence = mobxReact.observer(function TranscriptSequence({ asse
6894
7102
  else {
6895
7103
  setSequenceOptions(defaultSequenceOptions);
6896
7104
  }
6897
- // eslint-disable-next-line react-hooks/exhaustive-deps
7105
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
6898
7106
  }, [feature]);
6899
7107
  if (!(currentAssembly && refData)) {
6900
7108
  return null;
@@ -6957,19 +7165,6 @@ const TranscriptSequence = mobxReact.observer(function TranscriptSequence({ asse
6957
7165
  .join(';'), "(strand=", feature.strand === 1 ? '+' : '-', ";length=", getSequenceLength(sequenceSegments), ")", jsxRuntime.jsx("br", {}), wrapSequence(sequenceSegments, SEQUENCE_WRAP_LENGTH)] })] }));
6958
7166
  });
6959
7167
 
6960
- const StyledTextField = styled(NumberTextField)(() => ({
6961
- '&.MuiFormControl-root': {
6962
- marginTop: 0,
6963
- marginBottom: 0,
6964
- width: '100%',
6965
- },
6966
- '& .MuiInputBase-input': {
6967
- fontSize: 12,
6968
- height: 20,
6969
- padding: 1,
6970
- paddingLeft: 10,
6971
- },
6972
- }));
6973
7168
  const SequenceContainer = styled('div')({
6974
7169
  display: 'flex',
6975
7170
  justifyContent: 'center',
@@ -6991,6 +7186,187 @@ const StyledAccordionSummary$1 = styled(material.AccordionSummary)(() => ({
6991
7186
  maxHeight: 30,
6992
7187
  },
6993
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
+ }));
6994
7370
  const Strand = (props) => {
6995
7371
  const { strand } = props;
6996
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" })) }));
@@ -7009,7 +7385,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7009
7385
  const currentAssembly = session.apolloDataStore.assemblies.get(assembly);
7010
7386
  const refData = currentAssembly?.getByRefName(refName);
7011
7387
  const { changeManager } = session.apolloDataStore;
7012
- const seqRef = React.useRef(null);
7013
7388
  const { changeInProgress } = session;
7014
7389
  if (!refData) {
7015
7390
  return null;
@@ -7436,184 +7811,6 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7436
7811
  },
7437
7812
  ];
7438
7813
  };
7439
- const getTranslationSequence = () => {
7440
- let wholeSequence = '';
7441
- const [firstLocation] = cdsLocations;
7442
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
7443
- for (const loc of sortedCDSLocations) {
7444
- wholeSequence += refData.getSequence(loc.min, loc.max);
7445
- }
7446
- if (strand === -1) {
7447
- // Original: ACGCAT
7448
- // Complement: TGCGTA
7449
- // Reverse complement: ATGCGT
7450
- wholeSequence = util.revcom(wholeSequence);
7451
- }
7452
- const elements = [];
7453
- for (let codonGenomicPos = 0; codonGenomicPos < wholeSequence.length; codonGenomicPos += 3) {
7454
- const codonSeq = wholeSequence
7455
- .slice(codonGenomicPos, codonGenomicPos + 3)
7456
- .toUpperCase();
7457
- const protein = util.defaultCodonTable[codonSeq] || '&';
7458
- // highlight start codon and stop codons
7459
- if (codonSeq === 'ATG') {
7460
- elements.push(jsxRuntime.jsx(material.Typography, { component: 'span', style: {
7461
- backgroundColor: changeInProgress ? 'lightgray' : 'yellow',
7462
- cursor: 'pointer',
7463
- border: '1px solid black',
7464
- }, onClick: () => {
7465
- if (changeInProgress) {
7466
- return;
7467
- }
7468
- // NOTE: codonGenomicPos is important here for calculating the genomic location
7469
- // of the start codon. We are using the codonGenomicPos as the key in the typography
7470
- // elements to maintain the genomic postion of the codon start
7471
- const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
7472
- if (startCodonGenomicLocation !== cdsMin && strand === 1) {
7473
- updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
7474
- }
7475
- if (startCodonGenomicLocation !== cdsMax && strand === -1) {
7476
- updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
7477
- }
7478
- }, children: protein }, codonGenomicPos));
7479
- }
7480
- else if (['TAA', 'TAG', 'TGA'].includes(codonSeq)) {
7481
- elements.push(jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
7482
- }
7483
- else {
7484
- elements.push(
7485
- // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
7486
- jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
7487
- }
7488
- }
7489
- return elements;
7490
- };
7491
- // Codon position is the index of the start codon in the CDS genomic sequence
7492
- // Calculate the genomic location of the start codon based on the codon position in the CDS
7493
- const getCodonGenomicLocation = (codonGenomicPosition) => {
7494
- const [firstLocation] = cdsLocations;
7495
- let cdsLen = 0;
7496
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
7497
- // Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
7498
- // and codonGenomicPosition is 25
7499
- // ((10 - 0) + (30 - 20) + (50 - 40)) > 25
7500
- // So, start codon is in (40, 50)
7501
- // 40 + (25-20) = 45 is the genomic location of the start codon
7502
- if (strand === 1) {
7503
- for (const loc of sortedCDSLocations) {
7504
- const locLength = loc.max - loc.min;
7505
- if (cdsLen + locLength > codonGenomicPosition) {
7506
- return loc.min + (codonGenomicPosition - cdsLen);
7507
- }
7508
- cdsLen += locLength;
7509
- }
7510
- }
7511
- else if (strand === -1) {
7512
- for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
7513
- const loc = sortedCDSLocations[i];
7514
- const locLength = loc.max - loc.min;
7515
- if (cdsLen + locLength > codonGenomicPosition) {
7516
- return loc.max - (codonGenomicPosition - cdsLen);
7517
- }
7518
- cdsLen += locLength;
7519
- }
7520
- }
7521
- if (strand === 1) {
7522
- return cdsMin;
7523
- }
7524
- return cdsMax;
7525
- };
7526
- const trimTranslationSequence = () => {
7527
- const sequenceElements = getTranslationSequence();
7528
- const translationSequence = sequenceElements
7529
- .map((el) => el.props.children)
7530
- .join('');
7531
- if (translationSequence.startsWith('M') &&
7532
- translationSequence.endsWith('*')) {
7533
- return;
7534
- }
7535
- // NOTE: We are maintaining the genomic location of the codon start as the "key"
7536
- // in typography elements. See getTranslationSequence function
7537
- const translSeqCodonStartGenomicPosArr = [];
7538
- for (const el of sequenceElements) {
7539
- translSeqCodonStartGenomicPosArr.push({
7540
- codonGenomicPos: el.key,
7541
- sequenceLetter: el.props.children,
7542
- });
7543
- }
7544
- if (translSeqCodonStartGenomicPosArr.length === 0) {
7545
- return;
7546
- }
7547
- // Trim any sequence before first start codon and after stop codon
7548
- const startCodonIndex = translationSequence.indexOf('M');
7549
- const stopCodonIndex = translationSequence.indexOf('*');
7550
- const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos;
7551
- const stopCodonPos = translSeqCodonStartGenomicPosArr[stopCodonIndex].codonGenomicPos;
7552
- if (!startCodonPos || !stopCodonPos) {
7553
- return;
7554
- }
7555
- const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
7556
- let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
7557
- if (strand === 1) {
7558
- if (startCodonGenomicLoc > stopCodonGenomicLoc) {
7559
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
7560
- return;
7561
- }
7562
- let promise;
7563
- stopCodonGenomicLoc += 3; // move to end of stop codon
7564
- if (startCodonGenomicLoc !== cdsMin) {
7565
- promise = new Promise((resolve) => {
7566
- updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
7567
- resolve(true);
7568
- });
7569
- });
7570
- }
7571
- if (stopCodonGenomicLoc !== cdsMax) {
7572
- if (promise) {
7573
- void promise.then(() => {
7574
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
7575
- });
7576
- }
7577
- else {
7578
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
7579
- }
7580
- }
7581
- }
7582
- if (strand === -1) {
7583
- // reverse strand
7584
- if (startCodonGenomicLoc < stopCodonGenomicLoc) {
7585
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
7586
- return;
7587
- }
7588
- let promise;
7589
- stopCodonGenomicLoc -= 3; // move to end of stop codon
7590
- if (startCodonGenomicLoc !== cdsMax) {
7591
- promise = new Promise((resolve) => {
7592
- updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
7593
- resolve(true);
7594
- });
7595
- });
7596
- }
7597
- if (stopCodonGenomicLoc !== cdsMin) {
7598
- if (promise) {
7599
- void promise.then(() => {
7600
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
7601
- });
7602
- }
7603
- else {
7604
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
7605
- }
7606
- }
7607
- }
7608
- notify('Translation sequence trimmed to start and stop codons', 'success');
7609
- };
7610
- const onCopyClick = () => {
7611
- const seqDiv = seqRef.current;
7612
- if (!seqDiv) {
7613
- return;
7614
- }
7615
- void copyToClipboard(seqDiv);
7616
- };
7617
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) => {
7618
7815
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
7619
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) => {
@@ -7623,8 +7820,12 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7623
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) => {
7624
7821
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
7625
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) => {
7626
- 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 &&
7627
- 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) => {
7628
7829
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
7629
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) => {
7630
7831
  return handleExonLocationChange(loc.max, newLocation, feature, false);
@@ -7633,14 +7834,10 @@ const TranscriptWidgetEditLocation = mobxReact.observer(function TranscriptWidge
7633
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) => {
7634
7835
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
7635
7836
  }, disabled: changeInProgress }) })), jsxRuntime.jsx(material.Grid, { size: 1, children: index !== transcriptExonParts.length - 1 &&
7636
- getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography, { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
7637
- })] }), 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: {
7638
- marginTop: 10,
7639
- display: 'flex',
7640
- flexDirection: 'row',
7641
- alignItems: 'center',
7642
- gap: 10,
7643
- }, 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 }))] }));
7644
7841
  });
7645
7842
 
7646
7843
  const HeaderTableCell = styled(material.TableCell)(() => ({
@@ -7677,7 +7874,7 @@ const ApolloTranscriptDetailsWidget = mobxReact.observer(function ApolloTranscri
7677
7874
  const { assembly, feature, refName } = model;
7678
7875
  React.useEffect(() => {
7679
7876
  setPanelState(DEFAULT_PANELS);
7680
- // eslint-disable-next-line react-hooks/exhaustive-deps
7877
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
7681
7878
  }, [feature]);
7682
7879
  const session = util.getSession(model);
7683
7880
  const { pluginManager } = util.getEnv(session);
@@ -7736,8 +7933,8 @@ function handleFeatureTypeChange(changeManager, feature, oldType, newType) {
7736
7933
  typeName: 'TypeChange',
7737
7934
  changedIds: [featureId],
7738
7935
  featureId,
7739
- oldType: String(oldType),
7740
- newType: String(newType),
7936
+ oldType,
7937
+ newType,
7741
7938
  assembly: feature.assemblyId,
7742
7939
  });
7743
7940
  return changeManager.submit(change);
@@ -7833,7 +8030,7 @@ const NumberCell = mobxReact.observer(function NumberCell({ initialValue, notify
7833
8030
  if (initialValue !== value) {
7834
8031
  setValue(initialValue);
7835
8032
  }
7836
- // eslint-disable-next-line react-hooks/exhaustive-deps
8033
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
7837
8034
  }, [initialValue]);
7838
8035
  React.useEffect(() => {
7839
8036
  if (blur) {
@@ -8674,8 +8871,9 @@ function draw$3(display, ctx, gene, row, rowInFeature, block) {
8674
8871
  const [top, left, width] = getFeatureBox(display, gene, row, block);
8675
8872
  const height = getRowCount$2(display, gene) * apolloRowHeight;
8676
8873
  if (width > 2) {
8677
- let selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', { featureType: gene.type });
8678
- 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);
8679
8877
  ctx.fillStyle = selectedColor;
8680
8878
  ctx.fillRect(left, top, width, height);
8681
8879
  }
@@ -8948,7 +9146,7 @@ function getLayout(display, feature) {
8948
9146
  }
8949
9147
  return layout;
8950
9148
  }
8951
- function getContextMenuItems$1(display, feature) {
9149
+ function getContextMenuItems$1(display, transcript) {
8952
9150
  const { changeManager, regions, selectedFeature, session } = display;
8953
9151
  const [region] = regions;
8954
9152
  const currentAssemblyId = display.getAssemblyId(region.assemblyName);
@@ -8958,7 +9156,7 @@ function getContextMenuItems$1(display, feature) {
8958
9156
  label: 'Open transcript editor',
8959
9157
  onClick: () => {
8960
9158
  const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
8961
- feature,
9159
+ feature: transcript,
8962
9160
  assembly: currentAssemblyId,
8963
9161
  changeManager,
8964
9162
  refName: region.refName,
@@ -8978,7 +9176,7 @@ function getContextMenuItems$1(display, feature) {
8978
9176
  doneCallback();
8979
9177
  },
8980
9178
  changeManager,
8981
- sourceFeature: feature,
9179
+ sourceFeature: transcript,
8982
9180
  sourceAssemblyId: currentAssemblyId,
8983
9181
  selectedFeature,
8984
9182
  setSelectedFeature: (feature) => {
@@ -8998,7 +9196,7 @@ function getContextMenuItems$1(display, feature) {
8998
9196
  doneCallback();
8999
9197
  },
9000
9198
  changeManager,
9001
- sourceFeature: feature,
9199
+ sourceFeature: transcript,
9002
9200
  sourceAssemblyId: currentAssemblyId,
9003
9201
  selectedFeature,
9004
9202
  setSelectedFeature: (feature) => {
@@ -9008,6 +9206,31 @@ function getContextMenuItems$1(display, feature) {
9008
9206
  ]);
9009
9207
  },
9010
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
+ }
9011
9234
  return menuItems;
9012
9235
  }
9013
9236
  // False positive here, none of these functions use "this"
@@ -9185,7 +9408,7 @@ function colorCode(letter, theme) {
9185
9408
  letterUpper === 'C' ||
9186
9409
  letterUpper === 'G' ||
9187
9410
  letterUpper === 'T') {
9188
- return theme.palette.bases[letterUpper].main.toString();
9411
+ return theme.palette.bases[letterUpper].main;
9189
9412
  }
9190
9413
  return 'lightgray';
9191
9414
  }
@@ -9282,8 +9505,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9282
9505
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
9283
9506
  },
9284
9507
  get changeManager() {
9285
- return self.session.apolloDataStore
9286
- .changeManager;
9508
+ return self.session.apolloDataStore.changeManager;
9287
9509
  },
9288
9510
  getAssemblyId(assemblyName) {
9289
9511
  const { assemblyManager } = self.session;
@@ -9294,12 +9516,10 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9294
9516
  return assembly.name;
9295
9517
  },
9296
9518
  get selectedFeature() {
9297
- return self.session
9298
- .apolloSelectedFeature;
9519
+ return self.session.apolloSelectedFeature;
9299
9520
  },
9300
9521
  get hoveredFeature() {
9301
- return self.session
9302
- .apolloHoveredFeature;
9522
+ return self.session.apolloHoveredFeature;
9303
9523
  },
9304
9524
  }))
9305
9525
  .actions((self) => ({
@@ -9344,8 +9564,11 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9344
9564
  const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
9345
9565
  return {
9346
9566
  trackMenuItems() {
9347
- const { graphical, table, showCheckResults } = self;
9348
- 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 = [
9349
9572
  ...superTrackMenuItems(),
9350
9573
  {
9351
9574
  type: 'subMenu',
@@ -9397,7 +9620,7 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9397
9620
  {
9398
9621
  label: 'Filter features by type',
9399
9622
  onClick: () => {
9400
- const session = self.session;
9623
+ const { session } = self;
9401
9624
  self.session.queueDialog((doneCallback) => [
9402
9625
  FilterFeatures,
9403
9626
  {
@@ -9413,76 +9636,93 @@ function baseModelFactory$2(_pluginManager, configSchema) {
9413
9636
  ]);
9414
9637
  },
9415
9638
  },
9416
- {
9417
- label: 'Export annotations',
9418
- icon: Export,
9639
+ ];
9640
+ if (backendDriver instanceof LocalDriver) {
9641
+ items.push({
9642
+ label: 'Import annotations',
9643
+ icon: InputIcon,
9419
9644
  onClick: () => {
9420
- const [region] = self.regions;
9421
- const { assemblyName } = region;
9422
- const assembly = self.getAssemblyId(assemblyName);
9423
- if (!assembly) {
9424
- return;
9425
- }
9426
- const session = self.session;
9427
9645
  session.queueDialog((doneCallback) => [
9428
- DownloadGFF3,
9646
+ ImportAnnotations,
9429
9647
  {
9430
9648
  session,
9649
+ assemblyName,
9431
9650
  handleClose: () => {
9432
9651
  doneCallback();
9433
9652
  },
9434
- assembly,
9435
9653
  },
9436
9654
  ]);
9437
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
+ ]);
9438
9679
  },
9439
- {
9440
- label: 'View Change Log',
9441
- icon: TrackChangesIcon,
9442
- onClick: () => {
9443
- const [region] = self.regions;
9444
- const { assemblyName } = region;
9445
- const assembly = self.getAssemblyId(assemblyName);
9446
- if (!assembly) {
9447
- return;
9448
- }
9449
- const session = self.session;
9450
- session.queueDialog((doneCallback) => [
9451
- ViewChangeLog,
9452
- {
9453
- session,
9454
- handleClose: () => {
9455
- doneCallback();
9456
- },
9457
- 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();
9458
9697
  },
9459
- ]);
9460
- },
9698
+ assembly,
9699
+ },
9700
+ ]);
9461
9701
  },
9462
- {
9463
- label: 'View Check Results',
9464
- icon: FactCheckIcon,
9465
- onClick: () => {
9466
- const [region] = self.regions;
9467
- const { assemblyName } = region;
9468
- const assembly = self.getAssemblyId(assemblyName);
9469
- if (!assembly) {
9470
- return;
9471
- }
9472
- const session = self.session;
9473
- session.queueDialog((doneCallback) => [
9474
- ViewCheckResults,
9475
- {
9476
- session,
9477
- handleClose: () => {
9478
- doneCallback();
9479
- },
9480
- 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();
9481
9719
  },
9482
- ]);
9483
- },
9720
+ assembly,
9721
+ },
9722
+ ]);
9484
9723
  },
9485
- ];
9724
+ });
9725
+ return items;
9486
9726
  },
9487
9727
  };
9488
9728
  })
@@ -10548,8 +10788,7 @@ function baseModelFactory$1(_pluginManager, configSchema) {
10548
10788
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
10549
10789
  },
10550
10790
  get changeManager() {
10551
- return self.session.apolloDataStore
10552
- .changeManager;
10791
+ return self.session.apolloDataStore.changeManager;
10553
10792
  },
10554
10793
  getAssemblyId(assemblyName) {
10555
10794
  const { assemblyManager } = self.session;
@@ -10560,12 +10799,10 @@ function baseModelFactory$1(_pluginManager, configSchema) {
10560
10799
  return assembly.name;
10561
10800
  },
10562
10801
  get selectedFeature() {
10563
- return self.session
10564
- .apolloSelectedFeature;
10802
+ return self.session.apolloSelectedFeature;
10565
10803
  },
10566
10804
  get hoveredFeature() {
10567
- return self.session
10568
- .apolloHoveredFeature;
10805
+ return self.session.apolloHoveredFeature;
10569
10806
  },
10570
10807
  get height() {
10571
10808
  const { sequenceRowHeight } = self;
@@ -10777,11 +11014,9 @@ const FilterTranscripts = mobxReact.observer(function FilterTranscripts({ source
10777
11014
  onUpdate(newForms);
10778
11015
  setExcludedTranscripts(newForms);
10779
11016
  };
10780
- 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) => (
10781
- // eslint-disable-next-line react/jsx-key
10782
- 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: () => {
10783
11018
  handleChange(item);
10784
- }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }))) }) }) })] }) }));
11019
+ }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }, item))) }) }) })] }) }));
10785
11020
  });
10786
11021
 
10787
11022
  let forwardFillLight = null;
@@ -11627,8 +11862,7 @@ function baseModelFactory(_pluginManager, configSchema) {
11627
11862
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
11628
11863
  },
11629
11864
  get changeManager() {
11630
- return self.session.apolloDataStore
11631
- .changeManager;
11865
+ return self.session.apolloDataStore.changeManager;
11632
11866
  },
11633
11867
  getAssemblyId(assemblyName) {
11634
11868
  const { assemblyManager } = self.session;
@@ -11639,12 +11873,10 @@ function baseModelFactory(_pluginManager, configSchema) {
11639
11873
  return assembly.name;
11640
11874
  },
11641
11875
  get selectedFeature() {
11642
- return self.session
11643
- .apolloSelectedFeature;
11876
+ return self.session.apolloSelectedFeature;
11644
11877
  },
11645
11878
  get hoveredFeature() {
11646
- return self.session
11647
- .apolloHoveredFeature;
11879
+ return self.session.apolloHoveredFeature;
11648
11880
  },
11649
11881
  }))
11650
11882
  .actions((self) => ({
@@ -11772,7 +12004,7 @@ function baseModelFactory(_pluginManager, configSchema) {
11772
12004
  {
11773
12005
  label: 'Filter features by type',
11774
12006
  onClick: () => {
11775
- const session = self.session;
12007
+ const { session } = self;
11776
12008
  self.session.queueDialog((doneCallback) => [
11777
12009
  FilterFeatures,
11778
12010
  {
@@ -12210,17 +12442,12 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
12210
12442
  if (!featureTypeOntology) {
12211
12443
  throw new Error('featureTypeOntology is undefined');
12212
12444
  }
12213
- let foundFeature;
12214
- if (self.geneTrackRowNums.includes(row)) {
12215
- foundFeature = layoutRow.find((f) => f.feature.type == 'exon' &&
12445
+ const foundFeature = self.geneTrackRowNums.includes(row)
12446
+ ? layoutRow.find((f) => f.feature.type == 'exon' &&
12216
12447
  bp >= f.feature.min &&
12217
- bp <= f.feature.max);
12218
- if (!foundFeature) {
12219
- foundFeature = layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max);
12220
- }
12221
- }
12222
- else {
12223
- 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) => {
12224
12451
  const { feature } = f;
12225
12452
  const featureID = feature.attributes.get('gff_id')?.toString();
12226
12453
  const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript');
@@ -12242,7 +12469,6 @@ function mouseEventsModelIntermediateFactory(pluginManager, configSchema) {
12242
12469
  }
12243
12470
  return false;
12244
12471
  });
12245
- }
12246
12472
  if (!foundFeature) {
12247
12473
  return mousePosition;
12248
12474
  }
@@ -12566,26 +12792,32 @@ function CreateApolloAnnotation({ annotationFeature, assembly, handleClose, refS
12566
12792
  const apolloAssembly = apolloSessionModel.apolloDataStore.assemblies.get(assembly.name);
12567
12793
  const refSeq = apolloAssembly?.refSeqs.get(refSeqId);
12568
12794
  const features = refSeq?.getFeatures(region.start, region.end);
12569
- const getDestinationFeatures = () => {
12570
- const filteredFeatures = [];
12571
- for (const f of features ?? []) {
12572
- if (f.min > region.end || f.max < region.start) {
12573
- continue;
12574
- }
12575
- // Destination feature should be of type gene
12576
- if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
12577
- const featureSnapshot = mobxStateTree.getSnapshot(f);
12578
- filteredFeatures.push(featureSnapshot);
12579
- }
12580
- }
12581
- return filteredFeatures;
12582
- };
12583
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
+ };
12584
12810
  setErrorMessage('');
12585
- const features = getDestinationFeatures();
12586
- setDestinationFeatures(features);
12587
- setSelectedDestinationFeature(features[0]);
12588
- }, [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
+ ]);
12589
12821
  const handleParentFeatureCheck = (event) => {
12590
12822
  const isChecked = event.target.checked;
12591
12823
  setParentFeatureChecked(isChecked);
@@ -12865,8 +13097,21 @@ function annotationFromPileup(pluggableElement) {
12865
13097
  getAnnotationFeature(jbrowseFeature, refSeqId) {
12866
13098
  const start = jbrowseFeature.get('start');
12867
13099
  const end = jbrowseFeature.get('end');
12868
- const strand = jbrowseFeature.get('strand');
12869
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');
12870
13115
  const cigarData = jbrowseFeature.get('CIGAR');
12871
13116
  const ops = parseCigar(cigarData);
12872
13117
  let position = start;
@@ -12882,9 +13127,7 @@ function annotationFromPileup(pluggableElement) {
12882
13127
  case 'M':
12883
13128
  case '=':
12884
13129
  case 'X': {
12885
- if (currentExonStart === undefined) {
12886
- currentExonStart = position;
12887
- }
13130
+ currentExonStart ??= position;
12888
13131
  position += len;
12889
13132
  break;
12890
13133
  }
@@ -13298,7 +13541,7 @@ const LinearApolloDisplay = mobxReact.observer(function LinearApolloDisplay(prop
13298
13541
  : undefined, menuItems: contextMenuItems })] }))] }));
13299
13542
  });
13300
13543
 
13301
- 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, }) {
13302
13545
  const { apolloRowHeight, highestRow, showFeatureLabels } = model;
13303
13546
  const featureLabelSpacer = showFeatureLabels ? 2 : 1;
13304
13547
  return (jsxRuntime.jsx("div", { style: {
@@ -13370,8 +13613,7 @@ const LinearApolloSixFrameDisplay = mobxReact.observer(function LinearApolloSixF
13370
13613
  regionNumber: idx,
13371
13614
  })?.offsetPx ?? 0) - lgv.offsetPx;
13372
13615
  const [feature] = checkResult.featureIds;
13373
- if (!feature ||
13374
- !feature.parent ||
13616
+ if (!feature?.parent ||
13375
13617
  !looksLikeGene(feature.parent, session)) {
13376
13618
  return null;
13377
13619
  }
@@ -13623,18 +13865,15 @@ function addTopLevelMenus(rootModel) {
13623
13865
  }
13624
13866
  }
13625
13867
 
13626
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
13627
13868
  /* eslint-disable @typescript-eslint/no-confusing-void-expression */
13628
13869
  const ApolloJobModel = mobxStateTree.types
13629
13870
  .model('JobsManager', {})
13630
13871
  .views((self) => ({
13631
13872
  get jobStatusWidget() {
13632
13873
  const { widgets } = util.getSession(self);
13633
- let jobStatusWidget = widgets.get('JobsList');
13634
- if (!jobStatusWidget) {
13874
+ const jobStatusWidget = widgets.get('JobsList') ??
13635
13875
  // @ts-expect-error: addWidget function not detected on the session
13636
- jobStatusWidget = util.getSession(self).addWidget('JobsListWidget', 'JobsList');
13637
- }
13876
+ util.getSession(self).addWidget('JobsListWidget', 'JobsList');
13638
13877
  return jobStatusWidget;
13639
13878
  },
13640
13879
  }))
@@ -13913,19 +14152,15 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
13913
14152
  continue;
13914
14153
  }
13915
14154
  const { assemblyName, refName } = region;
13916
- let assembly = self.assemblies.get(assemblyName);
13917
- if (!assembly) {
13918
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
13919
- }
14155
+ const assembly = self.assemblies.get(assemblyName) ??
14156
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} });
13920
14157
  const [firstFeature] = features;
13921
- let ref = assembly.refSeqs.get(firstFeature.refSeq);
13922
- if (!ref) {
13923
- ref = assembly.refSeqs.put({
14158
+ const ref = assembly.refSeqs.get(firstFeature.refSeq) ??
14159
+ assembly.refSeqs.put({
13924
14160
  _id: firstFeature.refSeq,
13925
14161
  name: refName,
13926
14162
  features: {},
13927
14163
  });
13928
- }
13929
14164
  for (const feature of features) {
13930
14165
  if (!ref.features.has(feature._id)) {
13931
14166
  ref.features.put(feature);
@@ -13942,24 +14177,153 @@ function clientDataStoreFactory(AnnotationFeatureExtended) {
13942
14177
  }
13943
14178
  const { refSeq, seq } = yield backendDriver.getSequence(region);
13944
14179
  const { assemblyName, end, refName, start } = region;
13945
- let assembly = self.assemblies.get(assemblyName);
13946
- if (!assembly) {
13947
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
13948
- }
13949
- let ref = assembly.refSeqs.get(refSeq);
13950
- if (!ref) {
13951
- ref = assembly.refSeqs.put({
13952
- _id: refSeq,
13953
- name: refName,
13954
- sequence: [],
13955
- });
13956
- }
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: [] });
13957
14184
  ref.addSequence({ start, stop: end, sequence: seq });
13958
14185
  }
13959
14186
  }),
13960
14187
  }));
13961
14188
  }
13962
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
+
13963
14327
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
13964
14328
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
13965
14329
  function extendSession(pluginManager, sessionModel) {
@@ -14045,8 +14409,7 @@ function extendSession(pluginManager, sessionModel) {
14045
14409
  for (const block of dynamicBlocks.contentBlocks) {
14046
14410
  const { assemblyName, end, refName, start } = block;
14047
14411
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
14048
- if (assembly &&
14049
- assembly.backendDriverType === 'CollaborationServerDriver') {
14412
+ if (assembly?.backendDriverType === 'CollaborationServerDriver') {
14050
14413
  locations.push({ assemblyName, refName, start, end });
14051
14414
  }
14052
14415
  }
@@ -14105,8 +14468,8 @@ function extendSession(pluginManager, sessionModel) {
14105
14468
  for (const block of dynamicBlocks.contentBlocks) {
14106
14469
  const { assemblyName, end, refName, start } = block;
14107
14470
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
14108
- if (assembly &&
14109
- assembly.backendDriverType === 'CollaborationServerDriver') {
14471
+ if (assembly?.backendDriverType ===
14472
+ 'CollaborationServerDriver') {
14110
14473
  locations.push({ assemblyName, refName, start, end });
14111
14474
  }
14112
14475
  }
@@ -14226,6 +14589,37 @@ function extendSession(pluginManager, sessionModel) {
14226
14589
  beforeDestroy() {
14227
14590
  self.abortController.abort(new DOMException('Clean up Apollo session', 'AbortError'));
14228
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
+ },
14229
14623
  }))
14230
14624
  .views((self) => {
14231
14625
  const superTrackActions = self
@@ -14357,7 +14751,6 @@ function extendSession(pluginManager, sessionModel) {
14357
14751
  }
14358
14752
 
14359
14753
  /* eslint-disable @typescript-eslint/unbound-method */
14360
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
14361
14754
  /* eslint-disable @typescript-eslint/no-misused-promises */
14362
14755
  /* eslint-disable @typescript-eslint/no-explicit-any */
14363
14756
  function isApolloMessageData(data) {
@@ -14545,10 +14938,10 @@ class ApolloPlugin extends Plugin {
14545
14938
  });
14546
14939
  if (!inWebWorker) {
14547
14940
  pluginManager.addToExtensionPoint('Core-extendWorker', (handle) => {
14548
- if (!('on' in handle && handle.on)) {
14941
+ if (!('on' in handle.client && handle.client.on)) {
14549
14942
  return handle;
14550
14943
  }
14551
- handle.on('apollo', async (event) => {
14944
+ handle.client.on('apollo', async (event) => {
14552
14945
  if (!isApolloMessageData(event)) {
14553
14946
  return;
14554
14947
  }
@@ -14566,7 +14959,7 @@ class ApolloPlugin extends Plugin {
14566
14959
  break;
14567
14960
  }
14568
14961
  const { seq: sequence } = await backendDriver.getSequence(region);
14569
- handle.workers[0].postMessage({
14962
+ handle.worker.postMessage({
14570
14963
  apollo,
14571
14964
  messageId,
14572
14965
  sequence,
@@ -14584,7 +14977,7 @@ class ApolloPlugin extends Plugin {
14584
14977
  break;
14585
14978
  }
14586
14979
  const regions = await backendDriver.getRegions(assembly);
14587
- handle.workers[0].postMessage({
14980
+ handle.worker.postMessage({
14588
14981
  apollo,
14589
14982
  messageId,
14590
14983
  regions,
@@ -14602,7 +14995,7 @@ class ApolloPlugin extends Plugin {
14602
14995
  break;
14603
14996
  }
14604
14997
  const refNameAliases = await backendDriver.getRefNameAliases(assembly);
14605
- handle.workers[0].postMessage({
14998
+ handle.worker.postMessage({
14606
14999
  apollo,
14607
15000
  messageId,
14608
15001
  refNameAliases,