@apollo-annotation/jbrowse-plugin-apollo 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts +8 -2
  2. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts.map +1 -1
  3. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts +8 -3
  4. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts.map +1 -1
  5. package/dist/ApolloInternetAccount/configSchema.d.ts +44 -0
  6. package/dist/ApolloInternetAccount/configSchema.d.ts.map +1 -0
  7. package/dist/ApolloInternetAccount/model.d.ts +174 -0
  8. package/dist/ApolloInternetAccount/model.d.ts.map +1 -0
  9. package/dist/ApolloJobModel.d.ts.map +1 -1
  10. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts +8 -0
  11. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts.map +1 -0
  12. package/dist/ApolloSequenceAdapter/configSchema.d.ts +8 -0
  13. package/dist/ApolloSequenceAdapter/configSchema.d.ts.map +1 -0
  14. package/dist/ApolloTextSearchAdapter/configSchema.d.ts +20 -0
  15. package/dist/ApolloTextSearchAdapter/configSchema.d.ts.map +1 -0
  16. package/dist/BackendDrivers/CollaborationServerDriver.d.ts +1 -1
  17. package/dist/BackendDrivers/CollaborationServerDriver.d.ts.map +1 -1
  18. package/dist/BackendDrivers/LocalDriver/LocalDriver.d.ts.map +1 -1
  19. package/dist/FeatureDetailsWidget/ApolloFeatureDetailsWidget.d.ts.map +1 -1
  20. package/dist/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.d.ts.map +1 -1
  21. package/dist/FeatureDetailsWidget/Attributes.d.ts.map +1 -1
  22. package/dist/FeatureDetailsWidget/DefaultAttributeEditor.d.ts.map +1 -1
  23. package/dist/FeatureDetailsWidget/DefaultAttributeViewer.d.ts.map +1 -1
  24. package/dist/FeatureDetailsWidget/NumberTextField.d.ts.map +1 -1
  25. package/dist/FeatureDetailsWidget/StringTextField.d.ts.map +1 -1
  26. package/dist/FeatureDetailsWidget/TranscriptSequence.d.ts.map +1 -1
  27. package/dist/FeatureDetailsWidget/TranscriptWidgetEditLocation.d.ts.map +1 -1
  28. package/dist/FeatureDetailsWidget/Translation.d.ts +14 -0
  29. package/dist/FeatureDetailsWidget/Translation.d.ts.map +1 -0
  30. package/dist/LinearApolloDisplay/components/CheckResultWarnings.d.ts.map +1 -1
  31. package/dist/LinearApolloDisplay/configSchema.d.ts +2 -0
  32. package/dist/LinearApolloDisplay/configSchema.d.ts.map +1 -0
  33. package/dist/LinearApolloDisplay/glyphs/ExonGlyph.d.ts.map +1 -1
  34. package/dist/LinearApolloDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  35. package/dist/LinearApolloDisplay/glyphs/TranscriptGlyph.d.ts.map +1 -1
  36. package/dist/LinearApolloDisplay/stateModel/base.d.ts +1 -18
  37. package/dist/LinearApolloDisplay/stateModel/base.d.ts.map +1 -1
  38. package/dist/LinearApolloDisplay/stateModel/index.d.ts +1 -17
  39. package/dist/LinearApolloDisplay/stateModel/index.d.ts.map +1 -1
  40. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts +2 -19
  41. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts.map +1 -1
  42. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts +2 -34
  43. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  44. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts +2 -19
  45. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts.map +1 -1
  46. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts +2 -0
  47. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts.map +1 -0
  48. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts +1 -1
  49. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts.map +1 -1
  50. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/index.d.ts +1 -1
  51. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/rendering.d.ts +1 -1
  52. package/dist/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.d.ts.map +1 -1
  53. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts +2 -0
  54. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts.map +1 -0
  55. package/dist/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  56. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts +1 -1
  57. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts.map +1 -1
  58. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts +1 -1
  59. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts.map +1 -1
  60. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts +2 -3
  61. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts.map +1 -1
  62. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts +2 -2
  63. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  64. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts +2 -3
  65. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts.map +1 -1
  66. package/dist/OntologyManager/OntologyStore/fulltext.d.ts.map +1 -1
  67. package/dist/OntologyManager/index.d.ts +734 -0
  68. package/dist/OntologyManager/index.d.ts.map +1 -0
  69. package/dist/TabularEditor/HybridGrid/NumberCell.d.ts.map +1 -1
  70. package/dist/components/AddCodingSequence.d.ts +14 -0
  71. package/dist/components/AddCodingSequence.d.ts.map +1 -0
  72. package/dist/components/AddRefSeqAliases.d.ts.map +1 -1
  73. package/dist/components/CopyFeature.d.ts.map +1 -1
  74. package/dist/components/CreateApolloAnnotation.d.ts.map +1 -1
  75. package/dist/components/DeleteFeature.d.ts.map +1 -1
  76. package/dist/components/DownloadGFF3.d.ts.map +1 -1
  77. package/dist/components/DuplicateFeatureDialog.d.ts +12 -0
  78. package/dist/components/DuplicateFeatureDialog.d.ts.map +1 -0
  79. package/dist/components/ImportAnnotations.d.ts +9 -0
  80. package/dist/components/ImportAnnotations.d.ts.map +1 -0
  81. package/dist/components/ManageUsers.d.ts.map +1 -1
  82. package/dist/components/OntologyTermMultiSelect.d.ts.map +1 -1
  83. package/dist/components/ViewChangeLog.d.ts.map +1 -1
  84. package/dist/components/index.d.ts +2 -0
  85. package/dist/components/index.d.ts.map +1 -1
  86. package/dist/extensions/annotationFromPileup.d.ts.map +1 -1
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.esm.js +830 -434
  89. package/dist/index.esm.js.map +1 -1
  90. package/dist/jbrowse-plugin-apollo.cjs.development.js +825 -429
  91. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  92. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  93. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  94. package/dist/jbrowse-plugin-apollo.umd.development.js +863 -468
  95. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  96. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  97. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  98. package/dist/session/ClientDataStore.d.ts +2194 -0
  99. package/dist/session/ClientDataStore.d.ts.map +1 -0
  100. package/dist/session/handleApolloFeaturesUrlParam.d.ts +8 -0
  101. package/dist/session/handleApolloFeaturesUrlParam.d.ts.map +1 -0
  102. package/dist/session/session.d.ts +6788 -0
  103. package/dist/session/session.d.ts.map +1 -0
  104. package/dist/util/displayUtils.d.ts +36 -0
  105. package/dist/util/displayUtils.d.ts.map +1 -0
  106. package/dist/util/sequenceUtils.d.ts +2 -0
  107. package/dist/util/sequenceUtils.d.ts.map +1 -0
  108. package/package.json +14 -14
  109. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +73 -38
  110. package/src/ApolloInternetAccount/components/LoginButtons.tsx +31 -10
  111. package/src/ApolloInternetAccount/model.ts +8 -5
  112. package/src/ApolloJobModel.ts +3 -8
  113. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +1 -1
  114. package/src/BackendDrivers/CollaborationServerDriver.ts +7 -11
  115. package/src/BackendDrivers/LocalDriver/LocalDriver.ts +13 -4
  116. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +1 -0
  117. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +4 -2
  118. package/src/FeatureDetailsWidget/Attributes.tsx +1 -0
  119. package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +1 -0
  120. package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +1 -0
  121. package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -0
  122. package/src/FeatureDetailsWidget/StringTextField.tsx +1 -0
  123. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +2 -1
  124. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +17 -354
  125. package/src/FeatureDetailsWidget/Translation.tsx +358 -0
  126. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +4 -1
  127. package/src/LinearApolloDisplay/glyphs/ExonGlyph.ts +2 -5
  128. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +4 -6
  129. package/src/LinearApolloDisplay/glyphs/TranscriptGlyph.ts +39 -6
  130. package/src/LinearApolloDisplay/stateModel/base.ts +47 -23
  131. package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -5
  132. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +4 -1
  133. package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -5
  134. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +10 -10
  135. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +5 -3
  136. package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +1 -1
  137. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +2 -3
  138. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +12 -20
  139. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +10 -6
  140. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +38 -45
  141. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +1 -4
  142. package/src/OntologyManager/OntologyStore/fulltext.ts +0 -2
  143. package/src/OntologyManager/OntologyStore/index.ts +1 -1
  144. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
  145. package/src/TabularEditor/HybridGrid/NumberCell.tsx +2 -1
  146. package/src/components/AddAssemblyAliases.tsx +1 -1
  147. package/src/components/AddCodingSequence.tsx +255 -0
  148. package/src/components/AddFeature.tsx +1 -1
  149. package/src/components/AddRefSeqAliases.tsx +3 -2
  150. package/src/components/CopyFeature.tsx +9 -7
  151. package/src/components/CreateApolloAnnotation.tsx +28 -23
  152. package/src/components/DeleteAssembly.tsx +2 -2
  153. package/src/components/DeleteFeature.tsx +1 -4
  154. package/src/components/DownloadGFF3.tsx +4 -3
  155. package/src/components/DuplicateFeatureDialog.tsx +154 -0
  156. package/src/components/DuplicateTranscript.tsx +1 -1
  157. package/src/components/FilterTranscripts.tsx +1 -1
  158. package/src/components/ImportAnnotations.tsx +108 -0
  159. package/src/components/ManageUsers.tsx +1 -2
  160. package/src/components/OntologyTermAutocomplete.tsx +1 -3
  161. package/src/components/OntologyTermMultiSelect.tsx +3 -1
  162. package/src/components/ViewChangeLog.tsx +2 -1
  163. package/src/components/index.ts +2 -0
  164. package/src/extensions/annotationFromPileup.ts +34 -26
  165. package/src/index.ts +9 -8
  166. package/src/session/ClientDataStore.ts +16 -21
  167. package/src/session/handleApolloFeaturesUrlParam.ts +122 -0
  168. package/src/session/session.ts +49 -9
  169. package/src/util/annotationFeatureUtils.ts +1 -1
  170. package/src/util/displayUtils.ts +1 -1
  171. package/src/util/glyphUtils.ts +3 -3
  172. package/src/util/sequenceUtils.ts +29 -0
@@ -3107,12 +3107,10 @@
3107
3107
  }, {
3108
3108
  key: "addChild",
3109
3109
  value: function addChild(parentFeature, child) {
3110
- var _parentFeature$attrib;
3110
+ var _parentFeature$attrib, _parentFeature$childr;
3111
3111
  if (!((_parentFeature$attrib = parentFeature.attributes) !== null && _parentFeature$attrib !== void 0 && _parentFeature$attrib._id)) {
3112
- var attributes = parentFeature.attributes;
3113
- if (!attributes) {
3114
- attributes = {};
3115
- }
3112
+ var _parentFeature$attrib2 = parentFeature.attributes,
3113
+ attributes = _parentFeature$attrib2 === void 0 ? {} : _parentFeature$attrib2;
3116
3114
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
3117
3115
  attributes = _objectSpread2({
3118
3116
  _id: [parentFeature._id.toString()]
@@ -3120,9 +3118,7 @@
3120
3118
  parentFeature.attributes = attributes;
3121
3119
  }
3122
3120
  var _id = child._id;
3123
- if (!parentFeature.children) {
3124
- parentFeature.children = new Map();
3125
- }
3121
+ (_parentFeature$childr = parentFeature.children) !== null && _parentFeature$childr !== void 0 ? _parentFeature$childr : parentFeature.children = new Map();
3126
3122
  parentFeature.children.set(_id, _objectSpread2(_objectSpread2({
3127
3123
  allIds: []
3128
3124
  }, child), {}, {
@@ -4284,10 +4280,9 @@
4284
4280
  }, {
4285
4281
  key: "mergeFeatureIntoTranscriptOnServer",
4286
4282
  value: function mergeFeatureIntoTranscriptOnServer(secondFeatureChild, firstTranscript) {
4287
- var _this2 = this;
4288
- if (!firstTranscript.children) {
4289
- firstTranscript.children = new Map();
4290
- }
4283
+ var _firstTranscript$chil,
4284
+ _this2 = this;
4285
+ (_firstTranscript$chil = firstTranscript.children) !== null && _firstTranscript$chil !== void 0 ? _firstTranscript$chil : firstTranscript.children = new Map();
4291
4286
  var merged = false;
4292
4287
  var mrgChild;
4293
4288
  var toDelete;
@@ -4304,11 +4299,10 @@
4304
4299
  toDelete = true;
4305
4300
  }
4306
4301
  if (mrgChild.type === secondFeatureChild.type && mrgChild.type === firstFeatureChild.type && util$1.doesIntersect2(secondFeatureChild.min, secondFeatureChild.max, mrgChild.min, mrgChild.max) && util$1.doesIntersect2(firstFeatureChild.min, firstFeatureChild.max, mrgChild.min, mrgChild.max)) {
4302
+ var _mrgChild, _mrgChild$attributes;
4307
4303
  mrgChild.min = Math.min(secondFeatureChild.min, mrgChild.min, firstFeatureChild.min);
4308
4304
  mrgChild.max = Math.max(secondFeatureChild.max, mrgChild.max, firstFeatureChild.max);
4309
- if (!mrgChild.attributes) {
4310
- mrgChild.attributes = {};
4311
- }
4305
+ (_mrgChild$attributes = (_mrgChild = mrgChild).attributes) !== null && _mrgChild$attributes !== void 0 ? _mrgChild$attributes : _mrgChild.attributes = {};
4312
4306
  var mrgChildAttr = JSON.parse(JSON.stringify(mrgChild.attributes));
4313
4307
  if (!Object.keys(mrgChildAttr).includes('merged_with')) {
4314
4308
  mrgChildAttr.merged_with = [];
@@ -4371,9 +4365,8 @@
4371
4365
  }, {
4372
4366
  key: "mergeFeatureIntoTranscriptOnClient",
4373
4367
  value: function mergeFeatureIntoTranscriptOnClient(secondFeatureChild, firstTranscript) {
4374
- if (!firstTranscript.children) {
4375
- firstTranscript.children = new Map();
4376
- }
4368
+ var _firstTranscript$chil2;
4369
+ (_firstTranscript$chil2 = firstTranscript.children) !== null && _firstTranscript$chil2 !== void 0 ? _firstTranscript$chil2 : firstTranscript.children = new Map();
4377
4370
  var merged = false;
4378
4371
  var mrgChild;
4379
4372
  var toDelete;
@@ -5633,21 +5626,21 @@
5633
5626
  var _feature$attributes, _feature$attributes2;
5634
5627
  var gff_id = (_feature$attributes = feature.attributes) === null || _feature$attributes === void 0 || (_feature$attributes = _feature$attributes.gff_id) === null || _feature$attributes === void 0 ? void 0 : _feature$attributes.join(', ');
5635
5628
  if (gff_id) {
5636
- return "".concat(gff_id, " (_id: ").concat(feature._id.toString(), ")");
5629
+ return "".concat(gff_id, " (_id: ").concat(feature._id, ")");
5637
5630
  }
5638
5631
  var gff_name = (_feature$attributes2 = feature.attributes) === null || _feature$attributes2 === void 0 || (_feature$attributes2 = _feature$attributes2.gff_name) === null || _feature$attributes2 === void 0 ? void 0 : _feature$attributes2.join(', ');
5639
5632
  if (gff_name) {
5640
- return "".concat(gff_name, " (_id: ").concat(feature._id.toString(), ")");
5633
+ return "".concat(gff_name, " (_id: ").concat(feature._id, ")");
5641
5634
  }
5642
- return "_id: ".concat(feature._id.toString());
5635
+ return "_id: ".concat(feature._id);
5643
5636
  }
5644
5637
 
5645
- var STOP_CODONS;
5638
+ var STOP_CODONS$1;
5646
5639
  (function (STOP_CODONS) {
5647
5640
  STOP_CODONS[STOP_CODONS["TAG"] = 0] = "TAG";
5648
5641
  STOP_CODONS[STOP_CODONS["TAA"] = 1] = "TAA";
5649
5642
  STOP_CODONS[STOP_CODONS["TGA"] = 2] = "TGA";
5650
- })(STOP_CODONS || (STOP_CODONS = {}));
5643
+ })(STOP_CODONS$1 || (STOP_CODONS$1 = {}));
5651
5644
  var START_CODONS;
5652
5645
  (function (START_CODONS) {
5653
5646
  START_CODONS[START_CODONS["ATG"] = 0] = "ATG";
@@ -5748,8 +5741,8 @@
5748
5741
  return a.min < b.min ? -1 : 1;
5749
5742
  });
5750
5743
  var i = 0;
5751
- var currentStart = undefined;
5752
- var currentEnd = undefined;
5744
+ var currentStart;
5745
+ var currentEnd;
5753
5746
  for (var iloc = 0; iloc < sortedLocation.length; iloc++) {
5754
5747
  var loc = sortedLocation[iloc];
5755
5748
  var phase = loc.phase;
@@ -5819,20 +5812,20 @@
5819
5812
  name: CHECK_NAME$1,
5820
5813
  cause: CAUSES$1[CAUSES$1.MissingStartCodon],
5821
5814
  ids: ids,
5822
- refSeq: refSeq.toString(),
5815
+ refSeq: refSeq,
5823
5816
  start: cdsStart,
5824
5817
  end: cdsStart,
5825
5818
  message: "Unexpected start codon \"".concat(start_codon, "\" in feature \"").concat(getPrintableId(feature), "\": ")
5826
5819
  });
5827
5820
  }
5828
5821
  lastCodon = codons.at(-1); // Last codon is supposed to be a stop
5829
- if (lastCodon && !(lastCodon.toUpperCase() in STOP_CODONS)) {
5822
+ if (lastCodon && !(lastCodon.toUpperCase() in STOP_CODONS$1)) {
5830
5823
  checkResults.push({
5831
5824
  _id: new ObjectID().toHexString(),
5832
5825
  name: CHECK_NAME$1,
5833
5826
  cause: CAUSES$1[CAUSES$1.MissingStopCodon],
5834
5827
  ids: ids,
5835
- refSeq: refSeq.toString(),
5828
+ refSeq: refSeq,
5836
5829
  start: cdsEnd,
5837
5830
  end: cdsEnd,
5838
5831
  message: "Missing stop codon in feature \"".concat(getPrintableId(feature), "\"")
@@ -5844,7 +5837,7 @@
5844
5837
  name: CHECK_NAME$1,
5845
5838
  cause: CAUSES$1[CAUSES$1.MissingStopCodon],
5846
5839
  ids: ids,
5847
- refSeq: refSeq.toString(),
5840
+ refSeq: refSeq,
5848
5841
  start: cdsEnd,
5849
5842
  end: cdsEnd,
5850
5843
  message: "Missing stop codon in feature \"".concat(getPrintableId(feature), "\"")
@@ -5866,14 +5859,14 @@
5866
5859
  return _context3.a(3, 9);
5867
5860
  case 7:
5868
5861
  location = getOriginalCodonLocation(cdsLocation, strand, idx);
5869
- if (location && codon.toUpperCase() in STOP_CODONS) {
5862
+ if (location && codon.toUpperCase() in STOP_CODONS$1) {
5870
5863
  _location = _slicedToArray(location, 2), codonStart = _location[0], codonEnd = _location[1];
5871
5864
  checkResults.push({
5872
5865
  _id: new ObjectID().toHexString(),
5873
5866
  name: CHECK_NAME$1,
5874
5867
  cause: CAUSES$1[CAUSES$1.InternalStopCodon],
5875
5868
  ids: ids,
5876
- refSeq: refSeq.toString(),
5869
+ refSeq: refSeq,
5877
5870
  start: codonStart,
5878
5871
  end: codonEnd,
5879
5872
  message: "Internal stop codon in feature \"".concat(getPrintableId(feature), "\"")
@@ -6171,7 +6164,7 @@
6171
6164
  name: CHECK_NAME,
6172
6165
  cause: CAUSES[CAUSES.NonCanonicalSpliceSiteAtFivePrime],
6173
6166
  ids: [feature._id],
6174
- refSeq: feature.refSeq.toString(),
6167
+ refSeq: feature.refSeq,
6175
6168
  start: spliceSequence.fivePrimeMin,
6176
6169
  end: spliceSequence.fivePrimeMin + 2,
6177
6170
  message: "Unexpected 5\u2032 splice site in \"".concat(getPrintableId(feature), "\". Expected: ").concat(_toConsumableArray(VALID_FIVE_PRIME_SEQ).join('|'), ", got: ").concat(spliceSequence.fivePrimeSeq)
@@ -6183,7 +6176,7 @@
6183
6176
  name: CHECK_NAME,
6184
6177
  cause: CAUSES[CAUSES.NonCanonicalSpliceSiteAtThreePrime],
6185
6178
  ids: [feature._id],
6186
- refSeq: feature.refSeq.toString(),
6179
+ refSeq: feature.refSeq,
6187
6180
  start: spliceSequence.threePrimeMin,
6188
6181
  end: spliceSequence.threePrimeMin + 2,
6189
6182
  message: "Unexpected 3\u2032 splice site in \"".concat(getPrintableId(feature), "\". Expected: ").concat(_toConsumableArray(VALID_THREE_PRIME_SEQ).join('|'), ", got: ").concat(spliceSequence.threePrimeSeq)
@@ -6351,8 +6344,8 @@
6351
6344
  delete attributes.gff_score;
6352
6345
  }
6353
6346
  return {
6354
- start: Number(featureLoc.min) + 1,
6355
- end: Number(featureLoc.max),
6347
+ start: featureLoc.min + 1,
6348
+ end: featureLoc.max,
6356
6349
  seq_id: featureLoc.refSeq,
6357
6350
  source: source,
6358
6351
  type: featureLoc.type,
@@ -6557,7 +6550,7 @@
6557
6550
  return transcriptParts;
6558
6551
  }
6559
6552
  function getCdsLocations(feature) {
6560
- var transcriptParts = [];
6553
+ var transcriptParts;
6561
6554
  try {
6562
6555
  transcriptParts = getTranscriptParts(feature);
6563
6556
  } catch (_unused) {
@@ -7122,7 +7115,7 @@
7122
7115
  d: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z"
7123
7116
  }), 'Add');
7124
7117
 
7125
- var version = "1.0.0";
7118
+ var version = "1.1.0";
7126
7119
 
7127
7120
  const ApolloConfigSchema = configuration.ConfigurationSchema('ApolloInternetAccount', {
7128
7121
  baseURL: {
@@ -21815,9 +21808,7 @@
21815
21808
  result = customTermList;
21816
21809
  }
21817
21810
  }
21818
- if (!result) {
21819
- result = await ontologyStore.getAllTerms();
21820
- }
21811
+ result ??= await ontologyStore.getAllTerms();
21821
21812
  return filterTerms ? result.filter((element) => filterTerms(element)) : result;
21822
21813
  }
21823
21814
 
@@ -21998,7 +21989,7 @@
21998
21989
  setErrorMessage('No assemblies to copy to');
21999
21990
  return;
22000
21991
  }
22001
- const assembly = await assemblyManager.waitForAssembly(selectedAssemblyId);
21992
+ const assembly = (await assemblyManager.waitForAssembly(selectedAssemblyId));
22002
21993
  if (!assembly) {
22003
21994
  return;
22004
21995
  }
@@ -22027,7 +22018,7 @@
22027
22018
  event.preventDefault();
22028
22019
  setErrorMessage('');
22029
22020
  const featureLength = sourceFeature.length;
22030
- const assembly = await assemblyManager.waitForAssembly(selectedAssemblyId);
22021
+ const assembly = (await assemblyManager.waitForAssembly(selectedAssemblyId));
22031
22022
  if (!assembly) {
22032
22023
  setErrorMessage(`Assembly not found: ${selectedAssemblyId}.`);
22033
22024
  return;
@@ -22365,8 +22356,7 @@
22365
22356
  locationChanges.push(geneChange);
22366
22357
  }
22367
22358
  }
22368
- if (featureTypeOntology &&
22369
- featureTypeOntology.isTypeOf(sourceFeature.type, 'exon')) {
22359
+ if (featureTypeOntology?.isTypeOf(sourceFeature.type, 'exon')) {
22370
22360
  const cdsChange = trimCDS(sourceFeature);
22371
22361
  if (cdsChange) {
22372
22362
  if (cdsChange.typeName === 'DeleteFeatureChange') {
@@ -22619,7 +22609,7 @@
22619
22609
  return;
22620
22610
  }
22621
22611
  function isSelectedFeature(feature, selectedFeature) {
22622
- return Boolean(selectedFeature && feature._id === selectedFeature._id);
22612
+ return feature._id === selectedFeature?._id;
22623
22613
  }
22624
22614
  function makeFeatureLabel(feature) {
22625
22615
  let name;
@@ -23225,6 +23215,153 @@
23225
23215
  }, pageSizeOptions: [5], processRowUpdate: processRowUpdate, disableRowSelectionOnClick: true }) }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
23226
23216
  }
23227
23217
 
23218
+ const START_CODON = 'ATG';
23219
+ const STOP_CODONS = new Set(['TAA', 'TAG', 'TGA']);
23220
+ function findLongestOrf(sequence) {
23221
+ const starts = [
23222
+ null,
23223
+ null,
23224
+ null,
23225
+ ];
23226
+ let longest;
23227
+ for (let start = 0; start < sequence.length - 2; start++) {
23228
+ const codon = sequence.slice(start, start + 3).toUpperCase();
23229
+ const frame = (start % 3);
23230
+ if (codon === START_CODON && starts[frame] === null) {
23231
+ starts[frame] = start;
23232
+ }
23233
+ else if (STOP_CODONS.has(codon)) {
23234
+ const inFrameStart = starts[frame];
23235
+ if (inFrameStart === null) {
23236
+ continue;
23237
+ }
23238
+ const length = start - inFrameStart;
23239
+ if (!longest || longest[1] - longest[0] < length) {
23240
+ longest = [inFrameStart, start];
23241
+ }
23242
+ starts[frame] = null;
23243
+ }
23244
+ }
23245
+ return longest;
23246
+ }
23247
+
23248
+ // Returns the genomic half-open boundary for stitched position `pos`.
23249
+ // For strand +1: the inclusive start (min) of the nucleotide.
23250
+ // For strand -1: the exclusive end (max) of the nucleotide.
23251
+ // Symmetric usage:
23252
+ // plus: cdsMin = helper(orf[0]), cdsMax = helper(orf[1]+3)
23253
+ // minus: cdsMax = helper(orf[0]), cdsMin = helper(orf[1]+3)
23254
+ function stitchedToGenomicBound(pos, exons, strand) {
23255
+ let cum = 0;
23256
+ for (const exon of exons) {
23257
+ const len = exon.max - exon.min;
23258
+ if (pos <= cum + len) {
23259
+ const off = pos - cum;
23260
+ return strand === 1 ? exon.min + off : exon.max - off;
23261
+ }
23262
+ cum += len;
23263
+ }
23264
+ const last = exons.at(-1);
23265
+ if (!last) {
23266
+ throw new Error('No exons found');
23267
+ }
23268
+ return strand === 1 ? last.max : last.min;
23269
+ }
23270
+ function AddCodingSequence({ changeManager, handleClose, refName, session, sourceAssemblyId, sourceFeature, }) {
23271
+ const [method, setMethod] = React.useState('longest-orf');
23272
+ const [minInput, setMinInput] = React.useState('');
23273
+ const [maxInput, setMaxInput] = React.useState('');
23274
+ const [errorMessage, setErrorMessage] = React.useState('');
23275
+ async function onSubmit(event) {
23276
+ event.preventDefault();
23277
+ setErrorMessage('');
23278
+ let cdsMin;
23279
+ let cdsMax;
23280
+ try {
23281
+ if (method === 'longest-orf') {
23282
+ const exonParts = sourceFeature.transcriptExonParts.filter((p) => p.type === 'exon');
23283
+ if (exonParts.length === 0) {
23284
+ setErrorMessage('No exons found in this transcript');
23285
+ return;
23286
+ }
23287
+ const backendDriver = session.apolloDataStore.getBackendDriver(sourceAssemblyId);
23288
+ if (!backendDriver) {
23289
+ setErrorMessage('No backend driver found for this assembly');
23290
+ return;
23291
+ }
23292
+ let stitchedSequence = '';
23293
+ for (const exon of exonParts) {
23294
+ const { seq } = await backendDriver.getSequence({
23295
+ assemblyName: sourceAssemblyId,
23296
+ refName,
23297
+ start: exon.min,
23298
+ end: exon.max,
23299
+ });
23300
+ stitchedSequence += sourceFeature.strand === -1 ? util$1.revcom(seq) : seq;
23301
+ }
23302
+ const orf = findLongestOrf(stitchedSequence);
23303
+ if (!orf) {
23304
+ ;
23305
+ session.notify('No open reading frame found in this transcript', 'error');
23306
+ handleClose();
23307
+ return;
23308
+ }
23309
+ const strand = sourceFeature.strand ?? 1;
23310
+ if (strand === 1) {
23311
+ cdsMin = stitchedToGenomicBound(orf[0], exonParts, 1);
23312
+ cdsMax = stitchedToGenomicBound(orf[1] + 3, exonParts, 1);
23313
+ }
23314
+ else {
23315
+ cdsMax = stitchedToGenomicBound(orf[0], exonParts, -1);
23316
+ cdsMin = stitchedToGenomicBound(orf[1] + 3, exonParts, -1);
23317
+ }
23318
+ }
23319
+ else {
23320
+ cdsMin = Number(minInput) - 1;
23321
+ cdsMax = Number(maxInput);
23322
+ }
23323
+ const _id = new ObjectID().toHexString();
23324
+ const addedFeature = {
23325
+ _id,
23326
+ refSeq: sourceFeature.refSeq,
23327
+ min: cdsMin,
23328
+ max: cdsMax,
23329
+ type: 'CDS',
23330
+ };
23331
+ if (sourceFeature.strand) {
23332
+ addedFeature.strand = sourceFeature.strand;
23333
+ }
23334
+ const change = new AddFeatureChange({
23335
+ changedIds: [sourceFeature._id],
23336
+ typeName: 'AddFeatureChange',
23337
+ assembly: sourceAssemblyId,
23338
+ addedFeature,
23339
+ parentFeatureId: sourceFeature._id,
23340
+ });
23341
+ await changeManager.submit(change);
23342
+ session.apolloSetSelectedFeature(_id);
23343
+ handleClose();
23344
+ }
23345
+ catch (error) {
23346
+ setErrorMessage(error instanceof Error ? error.message : 'An unknown error occurred');
23347
+ }
23348
+ }
23349
+ const manualError = method === 'manual' &&
23350
+ Boolean(minInput) &&
23351
+ Boolean(maxInput) &&
23352
+ Number(maxInput) <= Number(minInput);
23353
+ const submitDisabled = method === 'manual' ? !(minInput && maxInput) || manualError : false;
23354
+ 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) => {
23355
+ void onSubmit(event);
23356
+ }, children: [jsxRuntime.jsxs(material.DialogContent, { style: { display: 'flex', flexDirection: 'column' }, children: [jsxRuntime.jsx(material.FormControl, { children: jsxRuntime.jsxs(material.RadioGroup, { value: method, onChange: (e) => {
23357
+ setMethod(e.target.value);
23358
+ }, 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) => {
23359
+ setMinInput(e.target.value);
23360
+ } }), jsxRuntime.jsx(material.TextField, { margin: "dense", id: "cds-max", label: "Max", type: "number", fullWidth: true, variant: "outlined", value: maxInput, onChange: (e) => {
23361
+ setMaxInput(e.target.value);
23362
+ }, 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] }));
23363
+ }
23364
+
23228
23365
  class BackendDriver {
23229
23366
  clientStore;
23230
23367
  constructor(clientStore) {
@@ -23297,7 +23434,7 @@
23297
23434
  async getSequence(region) {
23298
23435
  const session = util$1.getSession(this.clientStore);
23299
23436
  const { assemblyManager } = session;
23300
- const assembly = await assemblyManager.waitForAssembly(region.assemblyName);
23437
+ const assembly = (await assemblyManager.waitForAssembly(region.assemblyName));
23301
23438
  if (!assembly) {
23302
23439
  throw new Error(`Assembly not found: "${region.assemblyName}"`);
23303
23440
  }
@@ -23316,7 +23453,10 @@
23316
23453
  const adapter = new CLASS(
23317
23454
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
23318
23455
  adapterConf, undefined, pluginManager);
23319
- const seq = await adapter.getSequence(region);
23456
+ const seq = await adapter.getSequence({
23457
+ ...region,
23458
+ refName: assembly.getSeqAdapterRefName(region.refName),
23459
+ });
23320
23460
  if (!seq) {
23321
23461
  throw new Error(`Sequence not found: ${JSON.stringify(region)}`);
23322
23462
  }
@@ -23325,7 +23465,7 @@
23325
23465
  async getRegions(assemblyName) {
23326
23466
  const session = util$1.getSession(this.clientStore);
23327
23467
  const { assemblyManager } = session;
23328
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
23468
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
23329
23469
  if (!assembly) {
23330
23470
  throw new Error(`Assembly not found: "${assemblyName}"`);
23331
23471
  }
@@ -23341,7 +23481,7 @@
23341
23481
  async getRefNameAliases(assemblyName) {
23342
23482
  const session = util$1.getSession(this.clientStore);
23343
23483
  const { assemblyManager } = session;
23344
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
23484
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
23345
23485
  if (!assembly) {
23346
23486
  throw new Error(`Assembly not found: "${assemblyName}"`);
23347
23487
  }
@@ -24000,7 +24140,7 @@
24000
24140
  const localSessionId = makeUserSessionId(token);
24001
24141
  const changeManager = new ChangeManager(this.clientStore);
24002
24142
  // Save server last change sequence into session storage
24003
- internetAccount.setLastChangeSequenceNumber(Number(message.changeSequence));
24143
+ internetAccount.setLastChangeSequenceNumber(message.changeSequence);
24004
24144
  if (message.userSessionId === localSessionId) {
24005
24145
  return; // we did this change, no need to apply it again
24006
24146
  }
@@ -24048,14 +24188,10 @@
24048
24188
  const seq = await inFlightPromise;
24049
24189
  return { seq, refSeq };
24050
24190
  }
24051
- let apolloAssembly = this.clientStore.assemblies.get(assemblyName);
24052
- if (!apolloAssembly) {
24053
- apolloAssembly = this.clientStore.addAssembly(assemblyName);
24054
- }
24055
- let apolloRefSeq = apolloAssembly.refSeqs.get(refSeq);
24056
- if (!apolloRefSeq) {
24057
- apolloRefSeq = apolloAssembly.addRefSeq(refSeq, refName);
24058
- }
24191
+ const apolloAssembly = this.clientStore.assemblies.get(assemblyName) ??
24192
+ this.clientStore.addAssembly(assemblyName);
24193
+ const apolloRefSeq = apolloAssembly.refSeqs.get(refSeq) ??
24194
+ apolloAssembly.addRefSeq(refSeq, refName);
24059
24195
  const clientStoreSequence = apolloRefSeq.getSequence(start, end);
24060
24196
  if (clientStoreSequence.length === end - start) {
24061
24197
  return { seq: clientStoreSequence, refSeq };
@@ -24412,7 +24548,7 @@
24412
24548
  setType(NewFeature[value]);
24413
24549
  }
24414
24550
  };
24415
- let submitDisabled = Boolean(error) || !(start && end && type);
24551
+ let submitDisabled = error || !(start && end && type);
24416
24552
  if ((type === NewFeature.CUSTOM && !customType) ||
24417
24553
  (!strand && type === NewFeature.GENE_AND_SUBFEATURES) ||
24418
24554
  (!strand && type === NewFeature.TRANSCRIPT_AND_SUBFEATURES)) {
@@ -24433,7 +24569,7 @@
24433
24569
  function DeleteAssembly({ changeManager, handleClose, session, }) {
24434
24570
  const { internetAccounts } = mobxStateTree.getRoot(session);
24435
24571
  const [errorMessage, setErrorMessage] = React.useState('');
24436
- const [confirmDelete, setconfirmDelete] = React.useState(false);
24572
+ const [confirmDelete, setConfirmDelete] = React.useState(false);
24437
24573
  const [submitted, setSubmitted] = React.useState(false);
24438
24574
  const apolloInternetAccounts = internetAccounts.filter((ia) => ia.type === 'ApolloInternetAccount');
24439
24575
  if (apolloInternetAccounts.length === 0) {
@@ -24474,7 +24610,7 @@
24474
24610
  event.preventDefault();
24475
24611
  }
24476
24612
  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: () => {
24477
- setconfirmDelete(!confirmDelete);
24613
+ setConfirmDelete(!confirmDelete);
24478
24614
  } }), 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] }));
24479
24615
  }
24480
24616
 
@@ -25555,7 +25691,7 @@
25555
25691
  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" }) }));
25556
25692
  }
25557
25693
  function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
25558
- const [includeFASTA, setincludeFASTA] = React.useState(false);
25694
+ const [includeFASTA, setIncludeFASTA] = React.useState(false);
25559
25695
  const [errorMessage, setErrorMessage] = React.useState('');
25560
25696
  const { getInternetAccount } = session.apolloDataStore;
25561
25697
  const { assemblyManager } = session;
@@ -25604,7 +25740,7 @@
25604
25740
  window.open(exportUri, '_blank');
25605
25741
  }
25606
25742
  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: () => {
25607
- setincludeFASTA(!includeFASTA);
25743
+ setIncludeFASTA(!includeFASTA);
25608
25744
  }, 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] }));
25609
25745
  }
25610
25746
  function getAssemblyGFF3Stream(assemblyName) {
@@ -25643,6 +25779,49 @@
25643
25779
  db.close();
25644
25780
  }
25645
25781
 
25782
+ function ImportAnnotations({ assemblyName, handleClose, session, }) {
25783
+ const { changeManager } = session.apolloDataStore;
25784
+ const [file, setFile] = React.useState();
25785
+ const [errorMessage, setErrorMessage] = React.useState('');
25786
+ const [loading, setLoading] = React.useState(false);
25787
+ function handleChangeFile(e) {
25788
+ if (e.target.files) {
25789
+ setFile(e.target.files[0]);
25790
+ }
25791
+ }
25792
+ async function onSubmit(event) {
25793
+ event.preventDefault();
25794
+ if (!file) {
25795
+ return;
25796
+ }
25797
+ setLoading(true);
25798
+ setErrorMessage('');
25799
+ try {
25800
+ const stream = file
25801
+ .stream()
25802
+ .pipeThrough(new TransformStream(new esmExports.GFFTransformer({ parseSequences: false })));
25803
+ for await (const value of stream) {
25804
+ const annotationFeature = gff3ToAnnotationFeature(value);
25805
+ const change = new AddFeatureChange({
25806
+ changedIds: [annotationFeature._id],
25807
+ typeName: 'AddFeatureChange',
25808
+ assembly: assemblyName,
25809
+ addedFeature: annotationFeature,
25810
+ });
25811
+ await changeManager.submit(change);
25812
+ }
25813
+ handleClose();
25814
+ }
25815
+ catch (error) {
25816
+ setErrorMessage(String(error));
25817
+ }
25818
+ finally {
25819
+ setLoading(false);
25820
+ }
25821
+ }
25822
+ 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" })] })] })] }));
25823
+ }
25824
+
25646
25825
  function ImportFeatures({ changeManager, handleClose, session, }) {
25647
25826
  const { apolloDataStore } = session;
25648
25827
  const [file, setFile] = React.useState();
@@ -26046,7 +26225,7 @@
26046
26225
  });
26047
26226
  return newRow;
26048
26227
  }
26049
- 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) => {
26228
+ 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) => {
26050
26229
  setErrorMessage(String(error));
26051
26230
  } }) })] }), 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] }));
26052
26231
  }
@@ -26356,12 +26535,13 @@
26356
26535
  const [enableSubmit, setEnableSubmit] = React.useState(false);
26357
26536
  const [selectedAssembly, setSelectedAssembly] = React.useState();
26358
26537
  const [selectedRows, setSelectedRows] = React.useState([]);
26359
- const [refNameAliasMap, setRefNameAliasMap] = React.useState(new Map());
26538
+ const [refNameAliasMap, setRefNameAliasMap] = React.useState(() => new Map());
26360
26539
  const { apolloDataStore } = session;
26361
26540
  const { collaborationServerDriver } = apolloDataStore;
26362
26541
  const assemblies = collaborationServerDriver.getAssemblies();
26363
26542
  React.useEffect(() => {
26364
26543
  if (assemblies.length > 0) {
26544
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
26365
26545
  setSelectedAssembly(assemblies[0]);
26366
26546
  collaborationServerDriver
26367
26547
  .getRefNameAliases(assemblies[0].name)
@@ -26373,7 +26553,7 @@
26373
26553
  setErrorMessage('Error fetching refName aliases for assembly');
26374
26554
  });
26375
26555
  }
26376
- // eslint-disable-next-line react-hooks/exhaustive-deps
26556
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
26377
26557
  }, []);
26378
26558
  const initializeRefNameAliasMap = (refNameAliasesList) => {
26379
26559
  const initialMap = new Map();
@@ -26758,6 +26938,10 @@
26758
26938
  d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 4c1.93 0 3.5 1.57 3.5 3.5S13.93 13 12 13s-3.5-1.57-3.5-3.5S10.07 6 12 6m0 14c-2.03 0-4.43-.82-6.14-2.88C7.55 15.8 9.68 15 12 15s4.45.8 6.14 2.12C16.43 19.18 14.03 20 12 20"
26759
26939
  }), 'AccountCircle');
26760
26940
 
26941
+ var BusinessIcon = /*#__PURE__*/utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
26942
+ d: "M12 7V3H2v18h20V7zM6 19H4v-2h2zm0-4H4v-2h2zm0-4H4V9h2zm0-4H4V5h2zm4 12H8v-2h2zm0-4H8v-2h2zm0-4H8V9h2zm0-4H8V5h2zm10 12h-8v-2h2v-2h-2v-2h2v-2h-2V9h8zm-2-8h-2v2h2zm0 4h-2v2h2z"
26943
+ }), 'Business');
26944
+
26761
26945
  // Icon source: https://developers.google.com/identity/branding-guidelines
26762
26946
  function Google(props) {
26763
26947
  const { color } = props;
@@ -26782,17 +26966,23 @@
26782
26966
  }));
26783
26967
  function GoogleButton(props) {
26784
26968
  const { classes } = useStyles$d();
26785
- const { disabled } = props;
26786
- return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Google, { color: disabled ? 'disabled' : undefined }), ...props, children: "Sign in with Google" }));
26969
+ const { message } = props;
26970
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Google, {}), ...props, children: message }));
26787
26971
  }
26788
26972
  function MicrosoftButton(props) {
26789
26973
  const { classes } = useStyles$d();
26790
- const { disabled } = props;
26791
- return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Microsoft, { color: disabled ? 'disabled' : undefined }), ...props, children: "Sign in with Microsoft" }));
26974
+ const { message } = props;
26975
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(Microsoft, {}), ...props, children: message }));
26792
26976
  }
26793
26977
  function GuestButton(props) {
26794
26978
  const { classes } = useStyles$d();
26795
- return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(AccountCircleIcon, { fontSize: "small" }), ...props, children: "Continue as Guest" }));
26979
+ const { message } = props;
26980
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(AccountCircleIcon, { fontSize: "small" }), ...props, children: message }));
26981
+ }
26982
+ function GenericButton(props) {
26983
+ const { classes } = useStyles$d();
26984
+ const { message } = props;
26985
+ return (jsxRuntime.jsx(material.Button, { className: classes.loginButton, variant: "outlined", startIcon: jsxRuntime.jsx(BusinessIcon, { fontSize: "small" }), ...props, children: message }));
26796
26986
  }
26797
26987
 
26798
26988
  const useStyles$c = makeStyles()((theme) => ({
@@ -26804,7 +26994,7 @@
26804
26994
  const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
26805
26995
  const { classes } = useStyles$c();
26806
26996
  const [errorMessage, setErrorMessage] = React.useState('');
26807
- const [loginTypes, setLoginTypes] = React.useState([]);
26997
+ const [loginTypes, setLoginTypes] = React.useState(null);
26808
26998
  React.useEffect(() => {
26809
26999
  const controller = new AbortController();
26810
27000
  const { signal } = controller;
@@ -26818,6 +27008,9 @@
26818
27008
  }
26819
27009
  const data = (await response.json());
26820
27010
  setLoginTypes(data);
27011
+ if (data.length === 0) {
27012
+ setErrorMessage('No login types configured');
27013
+ }
26821
27014
  }
26822
27015
  getAuthTypes().catch((error) => {
26823
27016
  if (!isAbortException(error)) {
@@ -26829,26 +27022,35 @@
26829
27022
  };
26830
27023
  }, [baseURL]);
26831
27024
  function handleClick(authType) {
26832
- if (authType === 'google') {
26833
- handleClose('google');
26834
- }
26835
- else if (authType === 'microsoft') {
26836
- handleClose('microsoft');
26837
- }
26838
- else {
26839
- handleClose('guest');
26840
- }
26841
- }
26842
- const allowGoogle = loginTypes.includes('google');
26843
- const allowMicrosoft = loginTypes.includes('microsoft');
26844
- const allowGuest = loginTypes.includes('guest');
26845
- 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: () => {
26846
- handleClick('google');
26847
- } })) : null, allowMicrosoft ? (jsxRuntime.jsx(MicrosoftButton, { disabled: !allowMicrosoft, onClick: () => {
26848
- handleClick('microsoft');
26849
- } })) : null, allowGuest ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.Divider, { className: classes.divider }), jsxRuntime.jsx(GuestButton, { onClick: () => {
26850
- handleClick('guest');
26851
- } })] })) : null] }), jsxRuntime.jsx(material.DialogActions, { children: jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: () => {
27025
+ handleClose(authType);
27026
+ }
27027
+ if (loginTypes === null) {
27028
+ return 'Loading…';
27029
+ }
27030
+ const firstLoginType = loginTypes.at(0);
27031
+ if (firstLoginType && loginTypes.length === 1 && !firstLoginType.needsPopup) {
27032
+ handleClick(firstLoginType);
27033
+ }
27034
+ 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) => {
27035
+ if (loginType.name === 'google') {
27036
+ return (jsxRuntime.jsx(GoogleButton, { message: loginType.message, onClick: () => {
27037
+ handleClick(loginType);
27038
+ } }, loginType.name));
27039
+ }
27040
+ if (loginType.name === 'microsoft') {
27041
+ return (jsxRuntime.jsx(MicrosoftButton, { message: loginType.message, onClick: () => {
27042
+ handleClick(loginType);
27043
+ } }, loginType.name));
27044
+ }
27045
+ if (loginType.name === 'guest') {
27046
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(material.Divider, { className: classes.divider }), jsxRuntime.jsx(GuestButton, { message: loginType.message, onClick: () => {
27047
+ handleClick(loginType);
27048
+ } })] }, loginType.name));
27049
+ }
27050
+ return (jsxRuntime.jsx(GenericButton, { message: loginType.message, onClick: () => {
27051
+ handleClick(loginType);
27052
+ } }, loginType.name));
27053
+ }) }), jsxRuntime.jsx(material.DialogActions, { children: jsxRuntime.jsx(material.Button, { variant: "outlined", type: "submit", onClick: () => {
26852
27054
  handleClose();
26853
27055
  }, children: "Cancel" }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
26854
27056
  };
@@ -27044,13 +27246,13 @@
27044
27246
  },
27045
27247
  ]);
27046
27248
  });
27047
- if (authType !== 'guest') {
27249
+ if (authType.needsPopup) {
27048
27250
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
27049
- self.openAuthWindow(authType, resolve, reject);
27251
+ self.openAuthWindow(authType.name, resolve, reject);
27050
27252
  return;
27051
27253
  }
27052
27254
  const url = new URL('auth/login', baseURL);
27053
- const searchParams = new URLSearchParams({ type: authType });
27255
+ const searchParams = new URLSearchParams({ type: authType.name });
27054
27256
  url.search = searchParams.toString();
27055
27257
  const uri = url.toString();
27056
27258
  const response = await fetch(uri, { signal: self.controller.signal });
@@ -27171,7 +27373,7 @@
27171
27373
  socket.on('COMMON', (message) => {
27172
27374
  if ('checkResult' in message) {
27173
27375
  if (message.deleted) {
27174
- deleteCheckResult(message.checkResult._id.toString());
27376
+ deleteCheckResult(message.checkResult._id);
27175
27377
  }
27176
27378
  else {
27177
27379
  addCheckResult(message.checkResult);
@@ -27998,7 +28200,9 @@
27998
28200
  return newValues;
27999
28201
  });
28000
28202
  }
28001
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [newValues.map((value, idx) => (jsxRuntime.jsxs("div", { style: { display: 'flex' }, children: [jsxRuntime.jsx(StringTextField, { value: value, onChangeCommitted: (editedValue) => {
28203
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [newValues.map((value, idx) => (
28204
+ // eslint-disable-next-line @eslint-react/no-array-index-key
28205
+ jsxRuntime.jsxs("div", { style: { display: 'flex' }, children: [jsxRuntime.jsx(StringTextField, { value: value, onChangeCommitted: (editedValue) => {
28002
28206
  updateValue(idx, editedValue);
28003
28207
  }, variant: "outlined", fullWidth: true }), jsxRuntime.jsx(material.IconButton, { "aria-label": "delete", size: "medium", edge: "end", onClick: () => {
28004
28208
  deleteValue(idx);
@@ -28010,7 +28214,9 @@
28010
28214
  });
28011
28215
 
28012
28216
  function DefaultAttributeViewer({ values }) {
28013
- return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: values?.map((value, idx) => (jsxRuntime.jsx(material.Typography, { variant: "body2", color: "textSecondary", children: value }, `${idx}.${value}`))) }));
28217
+ return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: values?.map((value, idx) => (jsxRuntime.jsx(material.Typography
28218
+ // eslint-disable-next-line @eslint-react/no-array-index-key
28219
+ , { variant: "body2", color: "textSecondary", children: value }, `${idx}.${value}`))) }));
28014
28220
  }
28015
28221
 
28016
28222
  const useStyles$a = makeStyles()((theme) => ({
@@ -28316,6 +28522,7 @@
28316
28522
  const { classes } = useStyles$8();
28317
28523
  const [panelState, setPanelState] = React.useState(['attributes']);
28318
28524
  React.useEffect(() => {
28525
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
28319
28526
  setPanelState(['attributes']);
28320
28527
  }, [feature]);
28321
28528
  if (!(feature && currentAssembly)) {
@@ -28402,6 +28609,7 @@
28402
28609
  */
28403
28610
  get minWithChildren() {
28404
28611
  var min = self.min;
28612
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
28405
28613
  var children = self.children;
28406
28614
  if (!children) {
28407
28615
  return min;
@@ -28428,6 +28636,7 @@
28428
28636
  */
28429
28637
  get maxWithChildren() {
28430
28638
  var max = self.max;
28639
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
28431
28640
  var children = self.children;
28432
28641
  if (!children) {
28433
28642
  return max;
@@ -28536,6 +28745,7 @@
28536
28745
  if (!featureTypeOntology.isTypeOf(self.type, 'transcript') && !featureTypeOntology.isTypeOf(self.type, 'pseudogenic_transcript')) {
28537
28746
  throw new Error('Only features of type "transcript" or equivalent can calculate CDS locations');
28538
28747
  }
28748
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
28539
28749
  var children = self.children;
28540
28750
  if (!children) {
28541
28751
  throw new Error('no CDS or exons in transcript');
@@ -29440,7 +29650,7 @@
29440
29650
  else {
29441
29651
  setSequenceOptions(defaultSequenceOptions);
29442
29652
  }
29443
- // eslint-disable-next-line react-hooks/exhaustive-deps
29653
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
29444
29654
  }, [feature]);
29445
29655
  if (!(currentAssembly && refData)) {
29446
29656
  return null;
@@ -29503,6 +29713,10 @@
29503
29713
  .join(';'), "(strand=", feature.strand === 1 ? '+' : '-', ";length=", getSequenceLength(sequenceSegments), ")", jsxRuntime.jsx("br", {}), wrapSequence(sequenceSegments, SEQUENCE_WRAP_LENGTH)] })] }));
29504
29714
  });
29505
29715
 
29716
+ var RemoveIcon = /*#__PURE__*/utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
29717
+ d: "M19 13H5v-2h14z"
29718
+ }), 'Remove');
29719
+
29506
29720
  var ContentCopyIcon = /*#__PURE__*/utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
29507
29721
  d: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2m0 16H8V7h11z"
29508
29722
  }), 'ContentCopy');
@@ -29511,23 +29725,6 @@
29511
29725
  d: "M9.64 7.64c.23-.5.36-1.05.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c.59 0 1.14-.13 1.64-.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-.59-.13-1.14-.36-1.64L12 14l7 7h3v-1zM6 8c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2m0 12c-1.1 0-2-.89-2-2s.9-2 2-2 2 .89 2 2-.9 2-2 2m6-7.5c-.28 0-.5-.22-.5-.5s.22-.5.5-.5.5.22.5.5-.22.5-.5.5M19 3l-6 6 2 2 7-7V3z"
29512
29726
  }), 'ContentCut');
29513
29727
 
29514
- var RemoveIcon = /*#__PURE__*/utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
29515
- d: "M19 13H5v-2h14z"
29516
- }), 'Remove');
29517
-
29518
- const StyledTextField = newStyled(NumberTextField)(() => ({
29519
- '&.MuiFormControl-root': {
29520
- marginTop: 0,
29521
- marginBottom: 0,
29522
- width: '100%',
29523
- },
29524
- '& .MuiInputBase-input': {
29525
- fontSize: 12,
29526
- height: 20,
29527
- padding: 1,
29528
- paddingLeft: 10,
29529
- },
29530
- }));
29531
29728
  const SequenceContainer = newStyled('div')({
29532
29729
  display: 'flex',
29533
29730
  justifyContent: 'center',
@@ -29549,6 +29746,187 @@
29549
29746
  maxHeight: 30,
29550
29747
  },
29551
29748
  }));
29749
+ function Translation({ changeInProgress, cdsLocations, refData, strand,
29750
+ // eslint-disable-next-line @typescript-eslint/unbound-method
29751
+ updateCDSLocation, cdsMin, cdsMax, feature, session, }) {
29752
+ const seqRef = React.useRef(null);
29753
+ const { notify } = session;
29754
+ const cdsSequences = [];
29755
+ const [firstLocation] = cdsLocations;
29756
+ for (const loc of firstLocation) {
29757
+ const seq = refData.getSequence(loc.min, loc.max);
29758
+ cdsSequences.push(strand === -1 ? util$1.revcom(seq) : seq);
29759
+ }
29760
+ const cdsSequence = cdsSequences.join('');
29761
+ const proteinSequence = [];
29762
+ for (let codonGenomicPos = 0; codonGenomicPos < cdsSequence.length; codonGenomicPos += 3) {
29763
+ const codonSeq = cdsSequence
29764
+ .slice(codonGenomicPos, codonGenomicPos + 3)
29765
+ .toUpperCase();
29766
+ const protein = util$1.defaultCodonTable[codonSeq] || '&';
29767
+ proteinSequence.push(protein);
29768
+ }
29769
+ const onCopyClick = () => {
29770
+ const seqDiv = seqRef.current;
29771
+ if (!seqDiv) {
29772
+ return;
29773
+ }
29774
+ void copyToClipboard(seqDiv);
29775
+ };
29776
+ // Codon position is the index of the start codon in the CDS genomic sequence
29777
+ // Calculate the genomic location of the start codon based on the codon position in the CDS
29778
+ const getCodonGenomicLocation = (codonGenomicPosition) => {
29779
+ const [firstLocation] = cdsLocations;
29780
+ let cdsLen = 0;
29781
+ const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
29782
+ // Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
29783
+ // and codonGenomicPosition is 25
29784
+ // ((10 - 0) + (30 - 20) + (50 - 40)) > 25
29785
+ // So, start codon is in (40, 50)
29786
+ // 40 + (25-20) = 45 is the genomic location of the start codon
29787
+ if (strand === 1) {
29788
+ for (const loc of sortedCDSLocations) {
29789
+ const locLength = loc.max - loc.min;
29790
+ if (cdsLen + locLength > codonGenomicPosition) {
29791
+ return loc.min + (codonGenomicPosition - cdsLen);
29792
+ }
29793
+ cdsLen += locLength;
29794
+ }
29795
+ }
29796
+ else if (strand === -1) {
29797
+ for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
29798
+ const loc = sortedCDSLocations[i];
29799
+ const locLength = loc.max - loc.min;
29800
+ if (cdsLen + locLength > codonGenomicPosition) {
29801
+ return loc.max - (codonGenomicPosition - cdsLen);
29802
+ }
29803
+ cdsLen += locLength;
29804
+ }
29805
+ }
29806
+ if (strand === 1) {
29807
+ return cdsMin;
29808
+ }
29809
+ return cdsMax;
29810
+ };
29811
+ const trimTranslationSequence = () => {
29812
+ if ((proteinSequence.at(0) === 'M' && proteinSequence.at(-1) === '*') ||
29813
+ proteinSequence.length === 0) {
29814
+ return;
29815
+ }
29816
+ // Trim any sequence before first start codon and after stop codon
29817
+ const startCodonIndex = proteinSequence.indexOf('M');
29818
+ const stopCodonIndex = proteinSequence.lastIndexOf('*');
29819
+ const startCodonPos = startCodonIndex * 3;
29820
+ const stopCodonPos = stopCodonIndex * 3;
29821
+ const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
29822
+ let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
29823
+ if (strand === 1) {
29824
+ if (startCodonGenomicLoc > stopCodonGenomicLoc) {
29825
+ notify('Start codon genomic location should be less than stop codon genomic location', 'error');
29826
+ return;
29827
+ }
29828
+ let promise;
29829
+ stopCodonGenomicLoc += 3; // move to end of stop codon
29830
+ if (startCodonGenomicLoc !== cdsMin) {
29831
+ promise = new Promise((resolve) => {
29832
+ updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
29833
+ resolve(true);
29834
+ });
29835
+ });
29836
+ }
29837
+ if (stopCodonGenomicLoc !== cdsMax) {
29838
+ if (promise) {
29839
+ void promise.then(() => {
29840
+ updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
29841
+ });
29842
+ }
29843
+ else {
29844
+ updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
29845
+ }
29846
+ }
29847
+ }
29848
+ if (strand === -1) {
29849
+ // reverse strand
29850
+ if (startCodonGenomicLoc < stopCodonGenomicLoc) {
29851
+ notify('Start codon genomic location should be less than stop codon genomic location', 'error');
29852
+ return;
29853
+ }
29854
+ let promise;
29855
+ stopCodonGenomicLoc -= 3; // move to end of stop codon
29856
+ if (startCodonGenomicLoc !== cdsMax) {
29857
+ promise = new Promise((resolve) => {
29858
+ updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
29859
+ resolve(true);
29860
+ });
29861
+ });
29862
+ }
29863
+ if (stopCodonGenomicLoc !== cdsMin) {
29864
+ if (promise) {
29865
+ void promise.then(() => {
29866
+ updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
29867
+ });
29868
+ }
29869
+ else {
29870
+ updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
29871
+ }
29872
+ }
29873
+ }
29874
+ notify('Translation sequence trimmed to start and stop codons', 'success');
29875
+ };
29876
+ 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) => {
29877
+ const codonGenomicPos = idx * 3;
29878
+ if (protein === 'M') {
29879
+ return (jsxRuntime.jsx(material.Typography, { component: 'span', style: {
29880
+ backgroundColor: changeInProgress
29881
+ ? 'lightgray'
29882
+ : 'yellow',
29883
+ cursor: 'pointer',
29884
+ border: '1px solid black',
29885
+ }, onClick: () => {
29886
+ if (changeInProgress) {
29887
+ return;
29888
+ }
29889
+ // NOTE: codonGenomicPos is important here for calculating the genomic location
29890
+ // of the start codon. We are using the codonGenomicPos as the key in the typography
29891
+ // elements to maintain the genomic postion of the codon start
29892
+ const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
29893
+ if (startCodonGenomicLocation !== cdsMin &&
29894
+ strand === 1) {
29895
+ updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
29896
+ }
29897
+ if (startCodonGenomicLocation !== cdsMax &&
29898
+ strand === -1) {
29899
+ updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
29900
+ }
29901
+ }, children: protein }, codonGenomicPos));
29902
+ }
29903
+ if (protein === '*') {
29904
+ return (jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
29905
+ }
29906
+ // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
29907
+ return (jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
29908
+ }) }) }), jsxRuntime.jsxs("div", { style: {
29909
+ marginTop: 10,
29910
+ display: 'flex',
29911
+ flexDirection: 'row',
29912
+ alignItems: 'center',
29913
+ gap: 10,
29914
+ }, 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 } }) }) })] })] })] }) }));
29915
+ }
29916
+
29917
+ const StyledTextField = newStyled(NumberTextField)(() => ({
29918
+ '&.MuiFormControl-root': {
29919
+ marginTop: 0,
29920
+ marginBottom: 0,
29921
+ width: '100%',
29922
+ },
29923
+ '& .MuiInputBase-input': {
29924
+ fontSize: 12,
29925
+ height: 20,
29926
+ padding: 1,
29927
+ paddingLeft: 10,
29928
+ },
29929
+ }));
29552
29930
  const Strand = (props) => {
29553
29931
  const { strand } = props;
29554
29932
  return (jsxRuntime.jsx("div", { children: strand === 1 ? (jsxRuntime.jsx(AddIcon, {})) : strand === -1 ? (jsxRuntime.jsx(RemoveIcon, {})) : (jsxRuntime.jsx(material.Typography, { component: 'span', children: "N/A" })) }));
@@ -29567,7 +29945,6 @@
29567
29945
  const currentAssembly = session.apolloDataStore.assemblies.get(assembly);
29568
29946
  const refData = currentAssembly?.getByRefName(refName);
29569
29947
  const { changeManager } = session.apolloDataStore;
29570
- const seqRef = React.useRef(null);
29571
29948
  const { changeInProgress } = session;
29572
29949
  if (!refData) {
29573
29950
  return null;
@@ -29994,184 +30371,6 @@
29994
30371
  },
29995
30372
  ];
29996
30373
  };
29997
- const getTranslationSequence = () => {
29998
- let wholeSequence = '';
29999
- const [firstLocation] = cdsLocations;
30000
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
30001
- for (const loc of sortedCDSLocations) {
30002
- wholeSequence += refData.getSequence(loc.min, loc.max);
30003
- }
30004
- if (strand === -1) {
30005
- // Original: ACGCAT
30006
- // Complement: TGCGTA
30007
- // Reverse complement: ATGCGT
30008
- wholeSequence = util$1.revcom(wholeSequence);
30009
- }
30010
- const elements = [];
30011
- for (let codonGenomicPos = 0; codonGenomicPos < wholeSequence.length; codonGenomicPos += 3) {
30012
- const codonSeq = wholeSequence
30013
- .slice(codonGenomicPos, codonGenomicPos + 3)
30014
- .toUpperCase();
30015
- const protein = util$1.defaultCodonTable[codonSeq] || '&';
30016
- // highlight start codon and stop codons
30017
- if (codonSeq === 'ATG') {
30018
- elements.push(jsxRuntime.jsx(material.Typography, { component: 'span', style: {
30019
- backgroundColor: changeInProgress ? 'lightgray' : 'yellow',
30020
- cursor: 'pointer',
30021
- border: '1px solid black',
30022
- }, onClick: () => {
30023
- if (changeInProgress) {
30024
- return;
30025
- }
30026
- // NOTE: codonGenomicPos is important here for calculating the genomic location
30027
- // of the start codon. We are using the codonGenomicPos as the key in the typography
30028
- // elements to maintain the genomic postion of the codon start
30029
- const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
30030
- if (startCodonGenomicLocation !== cdsMin && strand === 1) {
30031
- updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
30032
- }
30033
- if (startCodonGenomicLocation !== cdsMax && strand === -1) {
30034
- updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
30035
- }
30036
- }, children: protein }, codonGenomicPos));
30037
- }
30038
- else if (['TAA', 'TAG', 'TGA'].includes(codonSeq)) {
30039
- elements.push(jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
30040
- }
30041
- else {
30042
- elements.push(
30043
- // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
30044
- jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
30045
- }
30046
- }
30047
- return elements;
30048
- };
30049
- // Codon position is the index of the start codon in the CDS genomic sequence
30050
- // Calculate the genomic location of the start codon based on the codon position in the CDS
30051
- const getCodonGenomicLocation = (codonGenomicPosition) => {
30052
- const [firstLocation] = cdsLocations;
30053
- let cdsLen = 0;
30054
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
30055
- // Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
30056
- // and codonGenomicPosition is 25
30057
- // ((10 - 0) + (30 - 20) + (50 - 40)) > 25
30058
- // So, start codon is in (40, 50)
30059
- // 40 + (25-20) = 45 is the genomic location of the start codon
30060
- if (strand === 1) {
30061
- for (const loc of sortedCDSLocations) {
30062
- const locLength = loc.max - loc.min;
30063
- if (cdsLen + locLength > codonGenomicPosition) {
30064
- return loc.min + (codonGenomicPosition - cdsLen);
30065
- }
30066
- cdsLen += locLength;
30067
- }
30068
- }
30069
- else if (strand === -1) {
30070
- for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
30071
- const loc = sortedCDSLocations[i];
30072
- const locLength = loc.max - loc.min;
30073
- if (cdsLen + locLength > codonGenomicPosition) {
30074
- return loc.max - (codonGenomicPosition - cdsLen);
30075
- }
30076
- cdsLen += locLength;
30077
- }
30078
- }
30079
- if (strand === 1) {
30080
- return cdsMin;
30081
- }
30082
- return cdsMax;
30083
- };
30084
- const trimTranslationSequence = () => {
30085
- const sequenceElements = getTranslationSequence();
30086
- const translationSequence = sequenceElements
30087
- .map((el) => el.props.children)
30088
- .join('');
30089
- if (translationSequence.startsWith('M') &&
30090
- translationSequence.endsWith('*')) {
30091
- return;
30092
- }
30093
- // NOTE: We are maintaining the genomic location of the codon start as the "key"
30094
- // in typography elements. See getTranslationSequence function
30095
- const translSeqCodonStartGenomicPosArr = [];
30096
- for (const el of sequenceElements) {
30097
- translSeqCodonStartGenomicPosArr.push({
30098
- codonGenomicPos: el.key,
30099
- sequenceLetter: el.props.children,
30100
- });
30101
- }
30102
- if (translSeqCodonStartGenomicPosArr.length === 0) {
30103
- return;
30104
- }
30105
- // Trim any sequence before first start codon and after stop codon
30106
- const startCodonIndex = translationSequence.indexOf('M');
30107
- const stopCodonIndex = translationSequence.indexOf('*');
30108
- const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos;
30109
- const stopCodonPos = translSeqCodonStartGenomicPosArr[stopCodonIndex].codonGenomicPos;
30110
- if (!startCodonPos || !stopCodonPos) {
30111
- return;
30112
- }
30113
- const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
30114
- let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
30115
- if (strand === 1) {
30116
- if (startCodonGenomicLoc > stopCodonGenomicLoc) {
30117
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
30118
- return;
30119
- }
30120
- let promise;
30121
- stopCodonGenomicLoc += 3; // move to end of stop codon
30122
- if (startCodonGenomicLoc !== cdsMin) {
30123
- promise = new Promise((resolve) => {
30124
- updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
30125
- resolve(true);
30126
- });
30127
- });
30128
- }
30129
- if (stopCodonGenomicLoc !== cdsMax) {
30130
- if (promise) {
30131
- void promise.then(() => {
30132
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
30133
- });
30134
- }
30135
- else {
30136
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
30137
- }
30138
- }
30139
- }
30140
- if (strand === -1) {
30141
- // reverse strand
30142
- if (startCodonGenomicLoc < stopCodonGenomicLoc) {
30143
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
30144
- return;
30145
- }
30146
- let promise;
30147
- stopCodonGenomicLoc -= 3; // move to end of stop codon
30148
- if (startCodonGenomicLoc !== cdsMax) {
30149
- promise = new Promise((resolve) => {
30150
- updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
30151
- resolve(true);
30152
- });
30153
- });
30154
- }
30155
- if (stopCodonGenomicLoc !== cdsMin) {
30156
- if (promise) {
30157
- void promise.then(() => {
30158
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
30159
- });
30160
- }
30161
- else {
30162
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
30163
- }
30164
- }
30165
- }
30166
- notify('Translation sequence trimmed to start and stop codons', 'success');
30167
- };
30168
- const onCopyClick = () => {
30169
- const seqDiv = seqRef.current;
30170
- if (!seqDiv) {
30171
- return;
30172
- }
30173
- void copyToClipboard(seqDiv);
30174
- };
30175
30374
  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) => {
30176
30375
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
30177
30376
  }, 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) => {
@@ -30181,8 +30380,12 @@
30181
30380
  }, 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) => {
30182
30381
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
30183
30382
  }, 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) => {
30184
- 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 &&
30185
- 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) => {
30383
+ return (
30384
+ // eslint-disable-next-line @eslint-react/no-array-index-key
30385
+ 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 &&
30386
+ getFivePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
30387
+ // eslint-disable-next-line @eslint-react/no-array-index-key
30388
+ , { 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) => {
30186
30389
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
30187
30390
  }, disabled: changeInProgress }) })) : (jsxRuntime.jsx(material.Grid, { size: 4, style: { padding: 0 }, children: jsxRuntime.jsx(StyledTextField, { margin: "dense", variant: "outlined", value: loc.max, onChangeCommitted: (newLocation) => {
30188
30391
  return handleExonLocationChange(loc.max, newLocation, feature, false);
@@ -30191,14 +30394,10 @@
30191
30394
  }, 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) => {
30192
30395
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
30193
30396
  }, disabled: changeInProgress }) })), jsxRuntime.jsx(material.Grid, { size: 1, children: index !== transcriptExonParts.length - 1 &&
30194
- getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography, { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
30195
- })] }), 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: {
30196
- marginTop: 10,
30197
- display: 'flex',
30198
- flexDirection: 'row',
30199
- alignItems: 'center',
30200
- gap: 10,
30201
- }, 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 } }) }) })] })] })] }) }))] }));
30397
+ getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography
30398
+ // eslint-disable-next-line @eslint-react/no-array-index-key
30399
+ , { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
30400
+ })] }), cdsPresent && (jsxRuntime.jsx(Translation, { changeInProgress: changeInProgress, cdsLocations: cdsLocations, refData: refData, strand: strand, updateCDSLocation: updateCDSLocation, cdsMin: cdsMin, cdsMax: cdsMax, feature: feature, session: session }))] }));
30202
30401
  });
30203
30402
 
30204
30403
  const HeaderTableCell = newStyled(material.TableCell)(() => ({
@@ -30235,7 +30434,7 @@
30235
30434
  const { assembly, feature, refName } = model;
30236
30435
  React.useEffect(() => {
30237
30436
  setPanelState(DEFAULT_PANELS);
30238
- // eslint-disable-next-line react-hooks/exhaustive-deps
30437
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
30239
30438
  }, [feature]);
30240
30439
  const session = util$1.getSession(model);
30241
30440
  const { pluginManager } = util$1.getEnv(session);
@@ -30294,8 +30493,8 @@
30294
30493
  typeName: 'TypeChange',
30295
30494
  changedIds: [featureId],
30296
30495
  featureId,
30297
- oldType: String(oldType),
30298
- newType: String(newType),
30496
+ oldType,
30497
+ newType,
30299
30498
  assembly: feature.assemblyId,
30300
30499
  });
30301
30500
  return changeManager.submit(change);
@@ -30391,7 +30590,7 @@
30391
30590
  if (initialValue !== value) {
30392
30591
  setValue(initialValue);
30393
30592
  }
30394
- // eslint-disable-next-line react-hooks/exhaustive-deps
30593
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
30395
30594
  }, [initialValue]);
30396
30595
  React.useEffect(() => {
30397
30596
  if (blur) {
@@ -31240,8 +31439,9 @@
31240
31439
  const [top, left, width] = getFeatureBox(display, gene, row, block);
31241
31440
  const height = getRowCount$2(display, gene) * apolloRowHeight;
31242
31441
  if (width > 2) {
31243
- let selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', { featureType: gene.type });
31244
- selectedColor = material.alpha(theme.palette.background.paper, 0.6);
31442
+ const selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', {
31443
+ featureType: gene.type,
31444
+ }) ?? material.alpha(theme.palette.background.paper, 0.6);
31245
31445
  ctx.fillStyle = selectedColor;
31246
31446
  ctx.fillRect(left, top, width, height);
31247
31447
  }
@@ -31514,7 +31714,7 @@
31514
31714
  }
31515
31715
  return layout;
31516
31716
  }
31517
- function getContextMenuItems$1(display, feature) {
31717
+ function getContextMenuItems$1(display, transcript) {
31518
31718
  const { changeManager, regions, selectedFeature, session } = display;
31519
31719
  const [region] = regions;
31520
31720
  const currentAssemblyId = display.getAssemblyId(region.assemblyName);
@@ -31524,7 +31724,7 @@
31524
31724
  label: 'Open transcript editor',
31525
31725
  onClick: () => {
31526
31726
  const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
31527
- feature,
31727
+ feature: transcript,
31528
31728
  assembly: currentAssemblyId,
31529
31729
  changeManager,
31530
31730
  refName: region.refName,
@@ -31544,7 +31744,7 @@
31544
31744
  doneCallback();
31545
31745
  },
31546
31746
  changeManager,
31547
- sourceFeature: feature,
31747
+ sourceFeature: transcript,
31548
31748
  sourceAssemblyId: currentAssemblyId,
31549
31749
  selectedFeature,
31550
31750
  setSelectedFeature: (feature) => {
@@ -31564,7 +31764,7 @@
31564
31764
  doneCallback();
31565
31765
  },
31566
31766
  changeManager,
31567
- sourceFeature: feature,
31767
+ sourceFeature: transcript,
31568
31768
  sourceAssemblyId: currentAssemblyId,
31569
31769
  selectedFeature,
31570
31770
  setSelectedFeature: (feature) => {
@@ -31574,6 +31774,31 @@
31574
31774
  ]);
31575
31775
  },
31576
31776
  });
31777
+ const { children } = transcript;
31778
+ if (!children) {
31779
+ return [];
31780
+ }
31781
+ const cdsChildren = [...children.values()].filter((child) => isCDSFeature(child, session));
31782
+ if (cdsChildren.length === 0) {
31783
+ menuItems.push({
31784
+ label: 'Add coding sequence',
31785
+ onClick: () => {
31786
+ session.queueDialog((doneCallback) => [
31787
+ AddCodingSequence,
31788
+ {
31789
+ session,
31790
+ handleClose: () => {
31791
+ doneCallback();
31792
+ },
31793
+ changeManager,
31794
+ sourceFeature: transcript,
31795
+ sourceAssemblyId: currentAssemblyId,
31796
+ refName: region.refName,
31797
+ },
31798
+ ]);
31799
+ },
31800
+ });
31801
+ }
31577
31802
  return menuItems;
31578
31803
  }
31579
31804
  // False positive here, none of these functions use "this"
@@ -31760,7 +31985,7 @@
31760
31985
  letterUpper === 'C' ||
31761
31986
  letterUpper === 'G' ||
31762
31987
  letterUpper === 'T') {
31763
- return theme.palette.bases[letterUpper].main.toString();
31988
+ return theme.palette.bases[letterUpper].main;
31764
31989
  }
31765
31990
  return 'lightgray';
31766
31991
  }
@@ -31857,8 +32082,7 @@
31857
32082
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
31858
32083
  },
31859
32084
  get changeManager() {
31860
- return self.session.apolloDataStore
31861
- .changeManager;
32085
+ return self.session.apolloDataStore.changeManager;
31862
32086
  },
31863
32087
  getAssemblyId(assemblyName) {
31864
32088
  const { assemblyManager } = self.session;
@@ -31869,12 +32093,10 @@
31869
32093
  return assembly.name;
31870
32094
  },
31871
32095
  get selectedFeature() {
31872
- return self.session
31873
- .apolloSelectedFeature;
32096
+ return self.session.apolloSelectedFeature;
31874
32097
  },
31875
32098
  get hoveredFeature() {
31876
- return self.session
31877
- .apolloHoveredFeature;
32099
+ return self.session.apolloHoveredFeature;
31878
32100
  },
31879
32101
  }))
31880
32102
  .actions((self) => ({
@@ -31919,8 +32141,11 @@
31919
32141
  const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
31920
32142
  return {
31921
32143
  trackMenuItems() {
31922
- const { graphical, table, showCheckResults } = self;
31923
- return [
32144
+ const { graphical, table, showCheckResults, session } = self;
32145
+ const [region] = self.regions;
32146
+ const { assemblyName } = region;
32147
+ const backendDriver = session.apolloDataStore.getBackendDriver(assemblyName);
32148
+ const items = [
31924
32149
  ...superTrackMenuItems(),
31925
32150
  {
31926
32151
  type: 'subMenu',
@@ -31972,7 +32197,7 @@
31972
32197
  {
31973
32198
  label: 'Filter features by type',
31974
32199
  onClick: () => {
31975
- const session = self.session;
32200
+ const { session } = self;
31976
32201
  self.session.queueDialog((doneCallback) => [
31977
32202
  FilterFeatures,
31978
32203
  {
@@ -31988,76 +32213,93 @@
31988
32213
  ]);
31989
32214
  },
31990
32215
  },
31991
- {
31992
- label: 'Export annotations',
31993
- icon: Export,
32216
+ ];
32217
+ if (backendDriver instanceof LocalDriver) {
32218
+ items.push({
32219
+ label: 'Import annotations',
32220
+ icon: InputIcon,
31994
32221
  onClick: () => {
31995
- const [region] = self.regions;
31996
- const { assemblyName } = region;
31997
- const assembly = self.getAssemblyId(assemblyName);
31998
- if (!assembly) {
31999
- return;
32000
- }
32001
- const session = self.session;
32002
32222
  session.queueDialog((doneCallback) => [
32003
- DownloadGFF3,
32223
+ ImportAnnotations,
32004
32224
  {
32005
32225
  session,
32226
+ assemblyName,
32006
32227
  handleClose: () => {
32007
32228
  doneCallback();
32008
32229
  },
32009
- assembly,
32010
32230
  },
32011
32231
  ]);
32012
32232
  },
32233
+ });
32234
+ }
32235
+ items.push({
32236
+ label: 'Export annotations',
32237
+ icon: Export,
32238
+ onClick: () => {
32239
+ const [region] = self.regions;
32240
+ const { assemblyName } = region;
32241
+ const assembly = self.getAssemblyId(assemblyName);
32242
+ if (!assembly) {
32243
+ return;
32244
+ }
32245
+ const { session } = self;
32246
+ session.queueDialog((doneCallback) => [
32247
+ DownloadGFF3,
32248
+ {
32249
+ session,
32250
+ handleClose: () => {
32251
+ doneCallback();
32252
+ },
32253
+ assembly,
32254
+ },
32255
+ ]);
32013
32256
  },
32014
- {
32015
- label: 'View Change Log',
32016
- icon: TrackChangesIcon,
32017
- onClick: () => {
32018
- const [region] = self.regions;
32019
- const { assemblyName } = region;
32020
- const assembly = self.getAssemblyId(assemblyName);
32021
- if (!assembly) {
32022
- return;
32023
- }
32024
- const session = self.session;
32025
- session.queueDialog((doneCallback) => [
32026
- ViewChangeLog,
32027
- {
32028
- session,
32029
- handleClose: () => {
32030
- doneCallback();
32031
- },
32032
- assembly,
32257
+ }, {
32258
+ label: 'View Change Log',
32259
+ icon: TrackChangesIcon,
32260
+ onClick: () => {
32261
+ const [region] = self.regions;
32262
+ const { assemblyName } = region;
32263
+ const assembly = self.getAssemblyId(assemblyName);
32264
+ if (!assembly) {
32265
+ return;
32266
+ }
32267
+ const { session } = self;
32268
+ session.queueDialog((doneCallback) => [
32269
+ ViewChangeLog,
32270
+ {
32271
+ session,
32272
+ handleClose: () => {
32273
+ doneCallback();
32033
32274
  },
32034
- ]);
32035
- },
32275
+ assembly,
32276
+ },
32277
+ ]);
32036
32278
  },
32037
- {
32038
- label: 'View Check Results',
32039
- icon: FactCheckIcon,
32040
- onClick: () => {
32041
- const [region] = self.regions;
32042
- const { assemblyName } = region;
32043
- const assembly = self.getAssemblyId(assemblyName);
32044
- if (!assembly) {
32045
- return;
32046
- }
32047
- const session = self.session;
32048
- session.queueDialog((doneCallback) => [
32049
- ViewCheckResults,
32050
- {
32051
- session,
32052
- handleClose: () => {
32053
- doneCallback();
32054
- },
32055
- assembly,
32279
+ }, {
32280
+ label: 'View Check Results',
32281
+ icon: FactCheckIcon,
32282
+ onClick: () => {
32283
+ const [region] = self.regions;
32284
+ const { assemblyName } = region;
32285
+ const assembly = self.getAssemblyId(assemblyName);
32286
+ if (!assembly) {
32287
+ return;
32288
+ }
32289
+ const { session } = self;
32290
+ session.queueDialog((doneCallback) => [
32291
+ ViewCheckResults,
32292
+ {
32293
+ session,
32294
+ handleClose: () => {
32295
+ doneCallback();
32056
32296
  },
32057
- ]);
32058
- },
32297
+ assembly,
32298
+ },
32299
+ ]);
32059
32300
  },
32060
- ];
32301
+ });
32302
+ return items;
32061
32303
  },
32062
32304
  };
32063
32305
  })
@@ -33123,8 +33365,7 @@
33123
33365
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
33124
33366
  },
33125
33367
  get changeManager() {
33126
- return self.session.apolloDataStore
33127
- .changeManager;
33368
+ return self.session.apolloDataStore.changeManager;
33128
33369
  },
33129
33370
  getAssemblyId(assemblyName) {
33130
33371
  const { assemblyManager } = self.session;
@@ -33135,12 +33376,10 @@
33135
33376
  return assembly.name;
33136
33377
  },
33137
33378
  get selectedFeature() {
33138
- return self.session
33139
- .apolloSelectedFeature;
33379
+ return self.session.apolloSelectedFeature;
33140
33380
  },
33141
33381
  get hoveredFeature() {
33142
- return self.session
33143
- .apolloHoveredFeature;
33382
+ return self.session.apolloHoveredFeature;
33144
33383
  },
33145
33384
  get height() {
33146
33385
  const { sequenceRowHeight } = self;
@@ -33352,11 +33591,9 @@
33352
33591
  onUpdate(newForms);
33353
33592
  setExcludedTranscripts(newForms);
33354
33593
  };
33355
- 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) => (
33356
- // eslint-disable-next-line react/jsx-key
33357
- jsxRuntime.jsx(material.FormControlLabel, { control: jsxRuntime.jsx(material.Checkbox, { checked: !excludedTranscripts.includes(item), onChange: () => {
33594
+ 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: () => {
33358
33595
  handleChange(item);
33359
- }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }))) }) }) })] }) }));
33596
+ }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }, item))) }) }) })] }) }));
33360
33597
  });
33361
33598
 
33362
33599
  let forwardFillLight = null;
@@ -34202,8 +34439,7 @@
34202
34439
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
34203
34440
  },
34204
34441
  get changeManager() {
34205
- return self.session.apolloDataStore
34206
- .changeManager;
34442
+ return self.session.apolloDataStore.changeManager;
34207
34443
  },
34208
34444
  getAssemblyId(assemblyName) {
34209
34445
  const { assemblyManager } = self.session;
@@ -34214,12 +34450,10 @@
34214
34450
  return assembly.name;
34215
34451
  },
34216
34452
  get selectedFeature() {
34217
- return self.session
34218
- .apolloSelectedFeature;
34453
+ return self.session.apolloSelectedFeature;
34219
34454
  },
34220
34455
  get hoveredFeature() {
34221
- return self.session
34222
- .apolloHoveredFeature;
34456
+ return self.session.apolloHoveredFeature;
34223
34457
  },
34224
34458
  }))
34225
34459
  .actions((self) => ({
@@ -34347,7 +34581,7 @@
34347
34581
  {
34348
34582
  label: 'Filter features by type',
34349
34583
  onClick: () => {
34350
- const session = self.session;
34584
+ const { session } = self;
34351
34585
  self.session.queueDialog((doneCallback) => [
34352
34586
  FilterFeatures,
34353
34587
  {
@@ -34785,17 +35019,12 @@
34785
35019
  if (!featureTypeOntology) {
34786
35020
  throw new Error('featureTypeOntology is undefined');
34787
35021
  }
34788
- let foundFeature;
34789
- if (self.geneTrackRowNums.includes(row)) {
34790
- foundFeature = layoutRow.find((f) => f.feature.type == 'exon' &&
35022
+ const foundFeature = self.geneTrackRowNums.includes(row)
35023
+ ? layoutRow.find((f) => f.feature.type == 'exon' &&
34791
35024
  bp >= f.feature.min &&
34792
- bp <= f.feature.max);
34793
- if (!foundFeature) {
34794
- foundFeature = layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max);
34795
- }
34796
- }
34797
- else {
34798
- foundFeature = layoutRow.find((f) => {
35025
+ bp <= f.feature.max) ??
35026
+ layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max)
35027
+ : layoutRow.find((f) => {
34799
35028
  const { feature } = f;
34800
35029
  const featureID = feature.attributes.get('gff_id')?.toString();
34801
35030
  const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript');
@@ -34817,7 +35046,6 @@
34817
35046
  }
34818
35047
  return false;
34819
35048
  });
34820
- }
34821
35049
  if (!foundFeature) {
34822
35050
  return mousePosition;
34823
35051
  }
@@ -35141,26 +35369,32 @@
35141
35369
  const apolloAssembly = apolloSessionModel.apolloDataStore.assemblies.get(assembly.name);
35142
35370
  const refSeq = apolloAssembly?.refSeqs.get(refSeqId);
35143
35371
  const features = refSeq?.getFeatures(region.start, region.end);
35144
- const getDestinationFeatures = () => {
35145
- const filteredFeatures = [];
35146
- for (const f of features ?? []) {
35147
- if (f.min > region.end || f.max < region.start) {
35148
- continue;
35149
- }
35150
- // Destination feature should be of type gene
35151
- if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
35152
- const featureSnapshot = mobxStateTree.getSnapshot(f);
35153
- filteredFeatures.push(featureSnapshot);
35154
- }
35155
- }
35156
- return filteredFeatures;
35157
- };
35158
35372
  React.useEffect(() => {
35373
+ const getDestinationFeatures = () => {
35374
+ const filteredFeatures = [];
35375
+ for (const f of features ?? []) {
35376
+ if (f.min > region.end || f.max < region.start) {
35377
+ continue;
35378
+ }
35379
+ // Destination feature should be of type gene
35380
+ if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
35381
+ const featureSnapshot = mobxStateTree.getSnapshot(f);
35382
+ filteredFeatures.push(featureSnapshot);
35383
+ }
35384
+ }
35385
+ return filteredFeatures;
35386
+ };
35159
35387
  setErrorMessage('');
35160
- const features = getDestinationFeatures();
35161
- setDestinationFeatures(features);
35162
- setSelectedDestinationFeature(features[0]);
35163
- }, [checkedChildrens, parentFeatureChecked, region]);
35388
+ const destinationFeatures = getDestinationFeatures();
35389
+ setDestinationFeatures(destinationFeatures);
35390
+ setSelectedDestinationFeature(destinationFeatures[0]);
35391
+ }, [
35392
+ checkedChildrens,
35393
+ featureTypeOntology,
35394
+ features,
35395
+ parentFeatureChecked,
35396
+ region,
35397
+ ]);
35164
35398
  const handleParentFeatureCheck = (event) => {
35165
35399
  const isChecked = event.target.checked;
35166
35400
  setParentFeatureChecked(isChecked);
@@ -35440,8 +35674,21 @@
35440
35674
  getAnnotationFeature(jbrowseFeature, refSeqId) {
35441
35675
  const start = jbrowseFeature.get('start');
35442
35676
  const end = jbrowseFeature.get('end');
35443
- const strand = jbrowseFeature.get('strand');
35444
35677
  const name = jbrowseFeature.get('name');
35678
+ let strand;
35679
+ const tags = jbrowseFeature.get('tags');
35680
+ const { ts, TS, XS } = tags;
35681
+ for (const tag of [ts, TS, XS]) {
35682
+ if (!strand && tag) {
35683
+ if ([1, '1', '+'].includes(tag)) {
35684
+ strand = 1;
35685
+ }
35686
+ else if ([-1, '-1', '-'].includes(tag)) {
35687
+ strand = -1;
35688
+ }
35689
+ }
35690
+ }
35691
+ strand ??= jbrowseFeature.get('strand');
35445
35692
  const cigarData = jbrowseFeature.get('CIGAR');
35446
35693
  const ops = parseCigar(cigarData);
35447
35694
  let position = start;
@@ -35457,9 +35704,7 @@
35457
35704
  case 'M':
35458
35705
  case '=':
35459
35706
  case 'X': {
35460
- if (currentExonStart === undefined) {
35461
- currentExonStart = position;
35462
- }
35707
+ currentExonStart ??= position;
35463
35708
  position += len;
35464
35709
  break;
35465
35710
  }
@@ -35885,7 +36130,7 @@
35885
36130
  : undefined, menuItems: contextMenuItems })] }))] }));
35886
36131
  });
35887
36132
 
35888
- const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx = 0, }) {
36133
+ const TrackLines = mobxReact.observer(function TrackLines({ model, hrStyle = { margin: 0, top: 0, color: 'black' }, idx, }) {
35889
36134
  const { apolloRowHeight, highestRow, showFeatureLabels } = model;
35890
36135
  const featureLabelSpacer = showFeatureLabels ? 2 : 1;
35891
36136
  return (jsxRuntime.jsx("div", { style: {
@@ -35957,8 +36202,7 @@
35957
36202
  regionNumber: idx,
35958
36203
  })?.offsetPx ?? 0) - lgv.offsetPx;
35959
36204
  const [feature] = checkResult.featureIds;
35960
- if (!feature ||
35961
- !feature.parent ||
36205
+ if (!feature?.parent ||
35962
36206
  !looksLikeGene(feature.parent, session)) {
35963
36207
  return null;
35964
36208
  }
@@ -36226,18 +36470,15 @@
36226
36470
  d: "M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3m3-10H5V5h10z"
36227
36471
  }), 'Save');
36228
36472
 
36229
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
36230
36473
  /* eslint-disable @typescript-eslint/no-confusing-void-expression */
36231
36474
  const ApolloJobModel = mobxStateTree.types
36232
36475
  .model('JobsManager', {})
36233
36476
  .views((self) => ({
36234
36477
  get jobStatusWidget() {
36235
36478
  const { widgets } = util$1.getSession(self);
36236
- let jobStatusWidget = widgets.get('JobsList');
36237
- if (!jobStatusWidget) {
36479
+ const jobStatusWidget = widgets.get('JobsList') ??
36238
36480
  // @ts-expect-error: addWidget function not detected on the session
36239
- jobStatusWidget = util$1.getSession(self).addWidget('JobsListWidget', 'JobsList');
36240
- }
36481
+ util$1.getSession(self).addWidget('JobsListWidget', 'JobsList');
36241
36482
  return jobStatusWidget;
36242
36483
  },
36243
36484
  }))
@@ -36516,19 +36757,15 @@
36516
36757
  continue;
36517
36758
  }
36518
36759
  const { assemblyName, refName } = region;
36519
- let assembly = self.assemblies.get(assemblyName);
36520
- if (!assembly) {
36521
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
36522
- }
36760
+ const assembly = self.assemblies.get(assemblyName) ??
36761
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} });
36523
36762
  const [firstFeature] = features;
36524
- let ref = assembly.refSeqs.get(firstFeature.refSeq);
36525
- if (!ref) {
36526
- ref = assembly.refSeqs.put({
36763
+ const ref = assembly.refSeqs.get(firstFeature.refSeq) ??
36764
+ assembly.refSeqs.put({
36527
36765
  _id: firstFeature.refSeq,
36528
36766
  name: refName,
36529
36767
  features: {},
36530
36768
  });
36531
- }
36532
36769
  for (const feature of features) {
36533
36770
  if (!ref.features.has(feature._id)) {
36534
36771
  ref.features.put(feature);
@@ -36545,24 +36782,153 @@
36545
36782
  }
36546
36783
  const { refSeq, seq } = yield backendDriver.getSequence(region);
36547
36784
  const { assemblyName, end, refName, start } = region;
36548
- let assembly = self.assemblies.get(assemblyName);
36549
- if (!assembly) {
36550
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
36551
- }
36552
- let ref = assembly.refSeqs.get(refSeq);
36553
- if (!ref) {
36554
- ref = assembly.refSeqs.put({
36555
- _id: refSeq,
36556
- name: refName,
36557
- sequence: [],
36558
- });
36559
- }
36785
+ const assembly = self.assemblies.get(assemblyName) ??
36786
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} });
36787
+ const ref = assembly.refSeqs.get(refSeq) ??
36788
+ assembly.refSeqs.put({ _id: refSeq, name: refName, sequence: [] });
36560
36789
  ref.addSequence({ start, stop: end, sequence: seq });
36561
36790
  }
36562
36791
  }),
36563
36792
  }));
36564
36793
  }
36565
36794
 
36795
+ function reassignIds(feature) {
36796
+ const newChildren = {};
36797
+ if (feature.children) {
36798
+ for (const child of Object.values(feature.children)) {
36799
+ const newChild = reassignIds(child);
36800
+ newChildren[newChild._id] = newChild;
36801
+ }
36802
+ }
36803
+ return {
36804
+ ...feature,
36805
+ _id: new ObjectID().toHexString(),
36806
+ children: feature.children ? newChildren : undefined,
36807
+ };
36808
+ }
36809
+ function DuplicateFeatureDialog({ assemblyName, changeManager, existingFeature, featureSnapshot, handleClose, }) {
36810
+ const [choice, setChoice] = React.useState('keepExisting');
36811
+ const [errorMessage, setErrorMessage] = React.useState('');
36812
+ const [submitted, setSubmitted] = React.useState(false);
36813
+ const handleSubmit = async () => {
36814
+ setErrorMessage('');
36815
+ setSubmitted(true);
36816
+ try {
36817
+ if (choice === 'useNew') {
36818
+ const deleteChange = new DeleteFeatureChange({
36819
+ typeName: 'DeleteFeatureChange',
36820
+ assembly: assemblyName,
36821
+ changedIds: [existingFeature._id],
36822
+ deletedFeature: existingFeature,
36823
+ });
36824
+ await changeManager.submit(deleteChange);
36825
+ const addChange = new AddFeatureChange({
36826
+ typeName: 'AddFeatureChange',
36827
+ assembly: assemblyName,
36828
+ changedIds: [featureSnapshot._id],
36829
+ addedFeature: featureSnapshot,
36830
+ });
36831
+ await changeManager.submit(addChange);
36832
+ }
36833
+ else if (choice === 'keepBoth') {
36834
+ const newSnapshot = reassignIds(featureSnapshot);
36835
+ const addChange = new AddFeatureChange({
36836
+ typeName: 'AddFeatureChange',
36837
+ assembly: assemblyName,
36838
+ changedIds: [newSnapshot._id],
36839
+ addedFeature: newSnapshot,
36840
+ });
36841
+ await changeManager.submit(addChange);
36842
+ }
36843
+ handleClose();
36844
+ }
36845
+ catch (error) {
36846
+ setErrorMessage(String(error));
36847
+ setSubmitted(false);
36848
+ }
36849
+ };
36850
+ 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) => {
36851
+ setChoice(e.target.value);
36852
+ }, 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" })] })] }));
36853
+ }
36854
+
36855
+ function fromUrlSafeBase64(urlSafeBase64) {
36856
+ let base64 = urlSafeBase64.replaceAll('-', '+').replaceAll('_', '/');
36857
+ const pad = base64.length % 4;
36858
+ if (pad) {
36859
+ base64 += '='.repeat(4 - pad);
36860
+ }
36861
+ return base64;
36862
+ }
36863
+ // Base64 decode + decompress
36864
+ async function decompress(encoded) {
36865
+ const binaryString = atob(encoded);
36866
+ // eslint-disable-next-line unicorn/prefer-code-point
36867
+ const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0));
36868
+ const stream = new Blob([bytes]).stream();
36869
+ const decompressionStream = new DecompressionStream('gzip');
36870
+ const decompressedStream = stream.pipeThrough(decompressionStream);
36871
+ const response = new Response(decompressedStream);
36872
+ const decompressed = await response.arrayBuffer();
36873
+ return JSON.parse(new TextDecoder().decode(decompressed));
36874
+ }
36875
+ async function handleApolloFeaturesUrlParam(encodedFeatures, apolloDataStore, assemblyManager, session) {
36876
+ const base64 = fromUrlSafeBase64(encodedFeatures);
36877
+ const decompressed = await decompress(base64);
36878
+ const featuresData = decompressed;
36879
+ for (const [assemblyName, featureSnapshots] of Object.entries(featuresData)) {
36880
+ const backendDriver = apolloDataStore.getBackendDriver(assemblyName);
36881
+ if (!(backendDriver instanceof LocalDriver)) {
36882
+ continue;
36883
+ }
36884
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
36885
+ if (!assembly) {
36886
+ throw new Error(`Assembly not found: "${assemblyName}"`);
36887
+ }
36888
+ const { regions } = assembly;
36889
+ if (!regions) {
36890
+ throw new Error(`Assembly not found: "${assemblyName}"`);
36891
+ }
36892
+ const refNames = regions.map((r) => r.refName);
36893
+ const db = await openDb(assemblyName, refNames);
36894
+ for (const featureSnapshot of featureSnapshots) {
36895
+ const storeName = `features-${featureSnapshot.refSeq}`;
36896
+ const existing = (await db.get(storeName, featureSnapshot._id));
36897
+ if (existing === undefined) {
36898
+ const change = new AddFeatureChange({
36899
+ typeName: 'AddFeatureChange',
36900
+ assembly: assemblyName,
36901
+ changedIds: [featureSnapshot._id],
36902
+ addedFeature: featureSnapshot,
36903
+ });
36904
+ await apolloDataStore.changeManager.submit(change);
36905
+ }
36906
+ else {
36907
+ // get rid of undefined values in JSON
36908
+ // eslint-disable-next-line unicorn/prefer-structured-clone
36909
+ const existingFeature = JSON.parse(JSON.stringify(existing));
36910
+ if (!equal(featureSnapshot, existingFeature)) {
36911
+ await new Promise((resolve) => {
36912
+ session.queueDialog((doneCallback) => [
36913
+ DuplicateFeatureDialog,
36914
+ {
36915
+ featureSnapshot,
36916
+ existingFeature,
36917
+ assemblyName,
36918
+ changeManager: apolloDataStore.changeManager,
36919
+ handleClose: () => {
36920
+ doneCallback();
36921
+ resolve();
36922
+ },
36923
+ },
36924
+ ]);
36925
+ });
36926
+ }
36927
+ }
36928
+ }
36929
+ }
36930
+ }
36931
+
36566
36932
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
36567
36933
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
36568
36934
  function extendSession(pluginManager, sessionModel) {
@@ -36648,8 +37014,7 @@
36648
37014
  for (const block of dynamicBlocks.contentBlocks) {
36649
37015
  const { assemblyName, end, refName, start } = block;
36650
37016
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
36651
- if (assembly &&
36652
- assembly.backendDriverType === 'CollaborationServerDriver') {
37017
+ if (assembly?.backendDriverType === 'CollaborationServerDriver') {
36653
37018
  locations.push({ assemblyName, refName, start, end });
36654
37019
  }
36655
37020
  }
@@ -36708,8 +37073,8 @@
36708
37073
  for (const block of dynamicBlocks.contentBlocks) {
36709
37074
  const { assemblyName, end, refName, start } = block;
36710
37075
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
36711
- if (assembly &&
36712
- assembly.backendDriverType === 'CollaborationServerDriver') {
37076
+ if (assembly?.backendDriverType ===
37077
+ 'CollaborationServerDriver') {
36713
37078
  locations.push({ assemblyName, refName, start, end });
36714
37079
  }
36715
37080
  }
@@ -36829,6 +37194,37 @@
36829
37194
  beforeDestroy() {
36830
37195
  self.abortController.abort(new DOMException('Clean up Apollo session', 'AbortError'));
36831
37196
  },
37197
+ }))
37198
+ .actions((self) => ({
37199
+ async afterCreate() {
37200
+ if (util$1.isElectron) {
37201
+ return;
37202
+ }
37203
+ const url = new URL(globalThis.location.href);
37204
+ const apolloFeatures = url.searchParams.get('apolloFeatures');
37205
+ if (!apolloFeatures) {
37206
+ return;
37207
+ }
37208
+ const { assemblyManager } = self;
37209
+ await handleApolloFeaturesUrlParam(apolloFeatures, self.apolloDataStore, assemblyManager, self);
37210
+ await new Promise((resolve) => setTimeout(resolve, 2000));
37211
+ const updatedURL = new URL(globalThis.location.href);
37212
+ updatedURL.searchParams.delete('apolloFeatures');
37213
+ globalThis.history.replaceState(null, '', updatedURL.toString());
37214
+ },
37215
+ afterAttach() {
37216
+ mobxStateTree.addDisposer(self, mobx.autorun((reaction) => {
37217
+ const { focusedViewId, activeWidgets } = self;
37218
+ if (!(focusedViewId && activeWidgets)) {
37219
+ return;
37220
+ }
37221
+ const trackSelector = activeWidgets.get('hierarchicalTrackSelector');
37222
+ // @ts-expect-error Don't have type for track selector
37223
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
37224
+ trackSelector?.setView(focusedViewId);
37225
+ reaction.dispose();
37226
+ }));
37227
+ },
36832
37228
  }))
36833
37229
  .views((self) => {
36834
37230
  const superTrackActions = self
@@ -36960,7 +37356,6 @@
36960
37356
  }
36961
37357
 
36962
37358
  /* eslint-disable @typescript-eslint/unbound-method */
36963
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
36964
37359
  /* eslint-disable @typescript-eslint/no-misused-promises */
36965
37360
  /* eslint-disable @typescript-eslint/no-explicit-any */
36966
37361
  function isApolloMessageData(data) {
@@ -37148,10 +37543,10 @@
37148
37543
  });
37149
37544
  if (!inWebWorker) {
37150
37545
  pluginManager.addToExtensionPoint('Core-extendWorker', (handle) => {
37151
- if (!('on' in handle && handle.on)) {
37546
+ if (!('on' in handle.client && handle.client.on)) {
37152
37547
  return handle;
37153
37548
  }
37154
- handle.on('apollo', async (event) => {
37549
+ handle.client.on('apollo', async (event) => {
37155
37550
  if (!isApolloMessageData(event)) {
37156
37551
  return;
37157
37552
  }
@@ -37169,7 +37564,7 @@
37169
37564
  break;
37170
37565
  }
37171
37566
  const { seq: sequence } = await backendDriver.getSequence(region);
37172
- handle.workers[0].postMessage({
37567
+ handle.worker.postMessage({
37173
37568
  apollo,
37174
37569
  messageId,
37175
37570
  sequence,
@@ -37187,7 +37582,7 @@
37187
37582
  break;
37188
37583
  }
37189
37584
  const regions = await backendDriver.getRegions(assembly);
37190
- handle.workers[0].postMessage({
37585
+ handle.worker.postMessage({
37191
37586
  apollo,
37192
37587
  messageId,
37193
37588
  regions,
@@ -37205,7 +37600,7 @@
37205
37600
  break;
37206
37601
  }
37207
37602
  const refNameAliases = await backendDriver.getRefNameAliases(assembly);
37208
- handle.workers[0].postMessage({
37603
+ handle.worker.postMessage({
37209
37604
  apollo,
37210
37605
  messageId,
37211
37606
  refNameAliases,