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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts +8 -2
  2. package/dist/ApolloInternetAccount/components/AuthTypeSelector.d.ts.map +1 -1
  3. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts +8 -3
  4. package/dist/ApolloInternetAccount/components/LoginButtons.d.ts.map +1 -1
  5. package/dist/ApolloInternetAccount/configSchema.d.ts +44 -0
  6. package/dist/ApolloInternetAccount/configSchema.d.ts.map +1 -0
  7. package/dist/ApolloInternetAccount/model.d.ts +174 -0
  8. package/dist/ApolloInternetAccount/model.d.ts.map +1 -0
  9. package/dist/ApolloJobModel.d.ts.map +1 -1
  10. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts +8 -0
  11. package/dist/ApolloRefNameAliasAdapter/configSchema.d.ts.map +1 -0
  12. package/dist/ApolloSequenceAdapter/configSchema.d.ts +8 -0
  13. package/dist/ApolloSequenceAdapter/configSchema.d.ts.map +1 -0
  14. package/dist/ApolloTextSearchAdapter/configSchema.d.ts +20 -0
  15. package/dist/ApolloTextSearchAdapter/configSchema.d.ts.map +1 -0
  16. package/dist/BackendDrivers/CollaborationServerDriver.d.ts +1 -1
  17. package/dist/BackendDrivers/CollaborationServerDriver.d.ts.map +1 -1
  18. package/dist/BackendDrivers/LocalDriver/LocalDriver.d.ts.map +1 -1
  19. package/dist/FeatureDetailsWidget/ApolloFeatureDetailsWidget.d.ts.map +1 -1
  20. package/dist/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.d.ts.map +1 -1
  21. package/dist/FeatureDetailsWidget/Attributes.d.ts.map +1 -1
  22. package/dist/FeatureDetailsWidget/DefaultAttributeEditor.d.ts.map +1 -1
  23. package/dist/FeatureDetailsWidget/DefaultAttributeViewer.d.ts.map +1 -1
  24. package/dist/FeatureDetailsWidget/NumberTextField.d.ts.map +1 -1
  25. package/dist/FeatureDetailsWidget/StringTextField.d.ts.map +1 -1
  26. package/dist/FeatureDetailsWidget/TranscriptSequence.d.ts.map +1 -1
  27. package/dist/FeatureDetailsWidget/TranscriptWidgetEditLocation.d.ts.map +1 -1
  28. package/dist/FeatureDetailsWidget/Translation.d.ts +14 -0
  29. package/dist/FeatureDetailsWidget/Translation.d.ts.map +1 -0
  30. package/dist/LinearApolloDisplay/components/CheckResultWarnings.d.ts.map +1 -1
  31. package/dist/LinearApolloDisplay/configSchema.d.ts +2 -0
  32. package/dist/LinearApolloDisplay/configSchema.d.ts.map +1 -0
  33. package/dist/LinearApolloDisplay/glyphs/ExonGlyph.d.ts.map +1 -1
  34. package/dist/LinearApolloDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  35. package/dist/LinearApolloDisplay/glyphs/TranscriptGlyph.d.ts.map +1 -1
  36. package/dist/LinearApolloDisplay/stateModel/base.d.ts +1 -18
  37. package/dist/LinearApolloDisplay/stateModel/base.d.ts.map +1 -1
  38. package/dist/LinearApolloDisplay/stateModel/index.d.ts +1 -17
  39. package/dist/LinearApolloDisplay/stateModel/index.d.ts.map +1 -1
  40. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts +2 -19
  41. package/dist/LinearApolloDisplay/stateModel/layouts.d.ts.map +1 -1
  42. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts +2 -34
  43. package/dist/LinearApolloDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  44. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts +2 -19
  45. package/dist/LinearApolloDisplay/stateModel/rendering.d.ts.map +1 -1
  46. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts +2 -0
  47. package/dist/LinearApolloReferenceSequenceDisplay/configSchema.d.ts.map +1 -0
  48. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts +1 -1
  49. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/base.d.ts.map +1 -1
  50. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/index.d.ts +1 -1
  51. package/dist/LinearApolloReferenceSequenceDisplay/stateModel/rendering.d.ts +1 -1
  52. package/dist/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.d.ts.map +1 -1
  53. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts +2 -0
  54. package/dist/LinearApolloSixFrameDisplay/configSchema.d.ts.map +1 -0
  55. package/dist/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.d.ts.map +1 -1
  56. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts +1 -1
  57. package/dist/LinearApolloSixFrameDisplay/stateModel/base.d.ts.map +1 -1
  58. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts +1 -1
  59. package/dist/LinearApolloSixFrameDisplay/stateModel/index.d.ts.map +1 -1
  60. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts +2 -3
  61. package/dist/LinearApolloSixFrameDisplay/stateModel/layouts.d.ts.map +1 -1
  62. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts +2 -2
  63. package/dist/LinearApolloSixFrameDisplay/stateModel/mouseEvents.d.ts.map +1 -1
  64. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts +2 -3
  65. package/dist/LinearApolloSixFrameDisplay/stateModel/rendering.d.ts.map +1 -1
  66. package/dist/OntologyManager/OntologyStore/fulltext.d.ts.map +1 -1
  67. package/dist/OntologyManager/index.d.ts +734 -0
  68. package/dist/OntologyManager/index.d.ts.map +1 -0
  69. package/dist/TabularEditor/HybridGrid/NumberCell.d.ts.map +1 -1
  70. package/dist/components/AddCodingSequence.d.ts +14 -0
  71. package/dist/components/AddCodingSequence.d.ts.map +1 -0
  72. package/dist/components/AddRefSeqAliases.d.ts.map +1 -1
  73. package/dist/components/CopyFeature.d.ts.map +1 -1
  74. package/dist/components/CreateApolloAnnotation.d.ts.map +1 -1
  75. package/dist/components/DeleteFeature.d.ts.map +1 -1
  76. package/dist/components/DownloadGFF3.d.ts.map +1 -1
  77. package/dist/components/DuplicateFeatureDialog.d.ts +12 -0
  78. package/dist/components/DuplicateFeatureDialog.d.ts.map +1 -0
  79. package/dist/components/ImportAnnotations.d.ts +9 -0
  80. package/dist/components/ImportAnnotations.d.ts.map +1 -0
  81. package/dist/components/ManageUsers.d.ts.map +1 -1
  82. package/dist/components/OntologyTermMultiSelect.d.ts.map +1 -1
  83. package/dist/components/ViewChangeLog.d.ts.map +1 -1
  84. package/dist/components/index.d.ts +2 -0
  85. package/dist/components/index.d.ts.map +1 -1
  86. package/dist/extensions/annotationFromPileup.d.ts.map +1 -1
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.esm.js +826 -433
  89. package/dist/index.esm.js.map +1 -1
  90. package/dist/jbrowse-plugin-apollo.cjs.development.js +821 -428
  91. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -1
  92. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +1 -1
  93. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -1
  94. package/dist/jbrowse-plugin-apollo.umd.development.js +859 -467
  95. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -1
  96. package/dist/jbrowse-plugin-apollo.umd.production.min.js +1 -1
  97. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -1
  98. package/dist/session/ClientDataStore.d.ts +2194 -0
  99. package/dist/session/ClientDataStore.d.ts.map +1 -0
  100. package/dist/session/handleApolloFeaturesUrlParam.d.ts +8 -0
  101. package/dist/session/handleApolloFeaturesUrlParam.d.ts.map +1 -0
  102. package/dist/session/session.d.ts +6788 -0
  103. package/dist/session/session.d.ts.map +1 -0
  104. package/dist/util/displayUtils.d.ts +36 -0
  105. package/dist/util/displayUtils.d.ts.map +1 -0
  106. package/dist/util/sequenceUtils.d.ts +2 -0
  107. package/dist/util/sequenceUtils.d.ts.map +1 -0
  108. package/package.json +14 -14
  109. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +73 -38
  110. package/src/ApolloInternetAccount/components/LoginButtons.tsx +31 -10
  111. package/src/ApolloInternetAccount/model.ts +8 -5
  112. package/src/ApolloJobModel.ts +3 -8
  113. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +1 -1
  114. package/src/BackendDrivers/CollaborationServerDriver.ts +7 -11
  115. package/src/BackendDrivers/LocalDriver/LocalDriver.ts +9 -3
  116. package/src/FeatureDetailsWidget/ApolloFeatureDetailsWidget.tsx +1 -0
  117. package/src/FeatureDetailsWidget/ApolloTranscriptDetailsWidget.tsx +4 -2
  118. package/src/FeatureDetailsWidget/Attributes.tsx +1 -0
  119. package/src/FeatureDetailsWidget/DefaultAttributeEditor.tsx +1 -0
  120. package/src/FeatureDetailsWidget/DefaultAttributeViewer.tsx +1 -0
  121. package/src/FeatureDetailsWidget/NumberTextField.tsx +1 -0
  122. package/src/FeatureDetailsWidget/StringTextField.tsx +1 -0
  123. package/src/FeatureDetailsWidget/TranscriptSequence.tsx +2 -1
  124. package/src/FeatureDetailsWidget/TranscriptWidgetEditLocation.tsx +17 -354
  125. package/src/FeatureDetailsWidget/Translation.tsx +358 -0
  126. package/src/LinearApolloDisplay/components/CheckResultWarnings.tsx +4 -1
  127. package/src/LinearApolloDisplay/glyphs/ExonGlyph.ts +2 -5
  128. package/src/LinearApolloDisplay/glyphs/GeneGlyph.ts +4 -6
  129. package/src/LinearApolloDisplay/glyphs/TranscriptGlyph.ts +39 -6
  130. package/src/LinearApolloDisplay/stateModel/base.ts +47 -23
  131. package/src/LinearApolloDisplay/stateModel/layouts.ts +7 -5
  132. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +4 -1
  133. package/src/LinearApolloDisplay/stateModel/rendering.ts +1 -5
  134. package/src/LinearApolloReferenceSequenceDisplay/stateModel/base.ts +10 -10
  135. package/src/LinearApolloSixFrameDisplay/components/LinearApolloSixFrameDisplay.tsx +5 -3
  136. package/src/LinearApolloSixFrameDisplay/components/TrackLines.tsx +1 -1
  137. package/src/LinearApolloSixFrameDisplay/glyphs/GeneGlyph.ts +2 -3
  138. package/src/LinearApolloSixFrameDisplay/stateModel/base.ts +12 -20
  139. package/src/LinearApolloSixFrameDisplay/stateModel/layouts.ts +10 -6
  140. package/src/LinearApolloSixFrameDisplay/stateModel/mouseEvents.ts +38 -45
  141. package/src/LinearApolloSixFrameDisplay/stateModel/rendering.ts +1 -4
  142. package/src/OntologyManager/OntologyStore/fulltext.ts +0 -2
  143. package/src/OntologyManager/OntologyStore/index.ts +1 -1
  144. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +2 -2
  145. package/src/TabularEditor/HybridGrid/NumberCell.tsx +2 -1
  146. package/src/components/AddAssemblyAliases.tsx +1 -1
  147. package/src/components/AddCodingSequence.tsx +255 -0
  148. package/src/components/AddFeature.tsx +1 -1
  149. package/src/components/AddRefSeqAliases.tsx +3 -2
  150. package/src/components/CopyFeature.tsx +9 -7
  151. package/src/components/CreateApolloAnnotation.tsx +28 -23
  152. package/src/components/DeleteAssembly.tsx +2 -2
  153. package/src/components/DeleteFeature.tsx +1 -4
  154. package/src/components/DownloadGFF3.tsx +4 -3
  155. package/src/components/DuplicateFeatureDialog.tsx +154 -0
  156. package/src/components/DuplicateTranscript.tsx +1 -1
  157. package/src/components/FilterTranscripts.tsx +1 -1
  158. package/src/components/ImportAnnotations.tsx +108 -0
  159. package/src/components/ManageUsers.tsx +1 -2
  160. package/src/components/OntologyTermAutocomplete.tsx +1 -3
  161. package/src/components/OntologyTermMultiSelect.tsx +3 -1
  162. package/src/components/ViewChangeLog.tsx +2 -1
  163. package/src/components/index.ts +2 -0
  164. package/src/extensions/annotationFromPileup.ts +34 -26
  165. package/src/index.ts +9 -8
  166. package/src/session/ClientDataStore.ts +16 -21
  167. package/src/session/handleApolloFeaturesUrlParam.ts +122 -0
  168. package/src/session/session.ts +49 -9
  169. package/src/util/annotationFeatureUtils.ts +1 -1
  170. package/src/util/displayUtils.ts +1 -1
  171. package/src/util/glyphUtils.ts +3 -3
  172. package/src/util/sequenceUtils.ts +29 -0
@@ -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.1";
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
  }
@@ -23328,7 +23465,7 @@
23328
23465
  async getRegions(assemblyName) {
23329
23466
  const session = util$1.getSession(this.clientStore);
23330
23467
  const { assemblyManager } = session;
23331
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
23468
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
23332
23469
  if (!assembly) {
23333
23470
  throw new Error(`Assembly not found: "${assemblyName}"`);
23334
23471
  }
@@ -23344,7 +23481,7 @@
23344
23481
  async getRefNameAliases(assemblyName) {
23345
23482
  const session = util$1.getSession(this.clientStore);
23346
23483
  const { assemblyManager } = session;
23347
- const assembly = await assemblyManager.waitForAssembly(assemblyName);
23484
+ const assembly = (await assemblyManager.waitForAssembly(assemblyName));
23348
23485
  if (!assembly) {
23349
23486
  throw new Error(`Assembly not found: "${assemblyName}"`);
23350
23487
  }
@@ -24003,7 +24140,7 @@
24003
24140
  const localSessionId = makeUserSessionId(token);
24004
24141
  const changeManager = new ChangeManager(this.clientStore);
24005
24142
  // Save server last change sequence into session storage
24006
- internetAccount.setLastChangeSequenceNumber(Number(message.changeSequence));
24143
+ internetAccount.setLastChangeSequenceNumber(message.changeSequence);
24007
24144
  if (message.userSessionId === localSessionId) {
24008
24145
  return; // we did this change, no need to apply it again
24009
24146
  }
@@ -24051,14 +24188,10 @@
24051
24188
  const seq = await inFlightPromise;
24052
24189
  return { seq, refSeq };
24053
24190
  }
24054
- let apolloAssembly = this.clientStore.assemblies.get(assemblyName);
24055
- if (!apolloAssembly) {
24056
- apolloAssembly = this.clientStore.addAssembly(assemblyName);
24057
- }
24058
- let apolloRefSeq = apolloAssembly.refSeqs.get(refSeq);
24059
- if (!apolloRefSeq) {
24060
- apolloRefSeq = apolloAssembly.addRefSeq(refSeq, refName);
24061
- }
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);
24062
24195
  const clientStoreSequence = apolloRefSeq.getSequence(start, end);
24063
24196
  if (clientStoreSequence.length === end - start) {
24064
24197
  return { seq: clientStoreSequence, refSeq };
@@ -24415,7 +24548,7 @@
24415
24548
  setType(NewFeature[value]);
24416
24549
  }
24417
24550
  };
24418
- let submitDisabled = Boolean(error) || !(start && end && type);
24551
+ let submitDisabled = error || !(start && end && type);
24419
24552
  if ((type === NewFeature.CUSTOM && !customType) ||
24420
24553
  (!strand && type === NewFeature.GENE_AND_SUBFEATURES) ||
24421
24554
  (!strand && type === NewFeature.TRANSCRIPT_AND_SUBFEATURES)) {
@@ -24436,7 +24569,7 @@
24436
24569
  function DeleteAssembly({ changeManager, handleClose, session, }) {
24437
24570
  const { internetAccounts } = mobxStateTree.getRoot(session);
24438
24571
  const [errorMessage, setErrorMessage] = React.useState('');
24439
- const [confirmDelete, setconfirmDelete] = React.useState(false);
24572
+ const [confirmDelete, setConfirmDelete] = React.useState(false);
24440
24573
  const [submitted, setSubmitted] = React.useState(false);
24441
24574
  const apolloInternetAccounts = internetAccounts.filter((ia) => ia.type === 'ApolloInternetAccount');
24442
24575
  if (apolloInternetAccounts.length === 0) {
@@ -24477,7 +24610,7 @@
24477
24610
  event.preventDefault();
24478
24611
  }
24479
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: () => {
24480
- setconfirmDelete(!confirmDelete);
24613
+ setConfirmDelete(!confirmDelete);
24481
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] }));
24482
24615
  }
24483
24616
 
@@ -25558,7 +25691,7 @@
25558
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" }) }));
25559
25692
  }
25560
25693
  function DownloadGFF3({ handleClose, session, assembly: assemblyName, }) {
25561
- const [includeFASTA, setincludeFASTA] = React.useState(false);
25694
+ const [includeFASTA, setIncludeFASTA] = React.useState(false);
25562
25695
  const [errorMessage, setErrorMessage] = React.useState('');
25563
25696
  const { getInternetAccount } = session.apolloDataStore;
25564
25697
  const { assemblyManager } = session;
@@ -25607,7 +25740,7 @@
25607
25740
  window.open(exportUri, '_blank');
25608
25741
  }
25609
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: () => {
25610
- setincludeFASTA(!includeFASTA);
25743
+ setIncludeFASTA(!includeFASTA);
25611
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] }));
25612
25745
  }
25613
25746
  function getAssemblyGFF3Stream(assemblyName) {
@@ -25646,6 +25779,49 @@
25646
25779
  db.close();
25647
25780
  }
25648
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
+
25649
25825
  function ImportFeatures({ changeManager, handleClose, session, }) {
25650
25826
  const { apolloDataStore } = session;
25651
25827
  const [file, setFile] = React.useState();
@@ -26049,7 +26225,7 @@
26049
26225
  });
26050
26226
  return newRow;
26051
26227
  }
26052
- 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) => {
26053
26229
  setErrorMessage(String(error));
26054
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] }));
26055
26231
  }
@@ -26359,12 +26535,13 @@
26359
26535
  const [enableSubmit, setEnableSubmit] = React.useState(false);
26360
26536
  const [selectedAssembly, setSelectedAssembly] = React.useState();
26361
26537
  const [selectedRows, setSelectedRows] = React.useState([]);
26362
- const [refNameAliasMap, setRefNameAliasMap] = React.useState(new Map());
26538
+ const [refNameAliasMap, setRefNameAliasMap] = React.useState(() => new Map());
26363
26539
  const { apolloDataStore } = session;
26364
26540
  const { collaborationServerDriver } = apolloDataStore;
26365
26541
  const assemblies = collaborationServerDriver.getAssemblies();
26366
26542
  React.useEffect(() => {
26367
26543
  if (assemblies.length > 0) {
26544
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
26368
26545
  setSelectedAssembly(assemblies[0]);
26369
26546
  collaborationServerDriver
26370
26547
  .getRefNameAliases(assemblies[0].name)
@@ -26376,7 +26553,7 @@
26376
26553
  setErrorMessage('Error fetching refName aliases for assembly');
26377
26554
  });
26378
26555
  }
26379
- // eslint-disable-next-line react-hooks/exhaustive-deps
26556
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
26380
26557
  }, []);
26381
26558
  const initializeRefNameAliasMap = (refNameAliasesList) => {
26382
26559
  const initialMap = new Map();
@@ -26761,6 +26938,10 @@
26761
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"
26762
26939
  }), 'AccountCircle');
26763
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
+
26764
26945
  // Icon source: https://developers.google.com/identity/branding-guidelines
26765
26946
  function Google(props) {
26766
26947
  const { color } = props;
@@ -26785,17 +26966,23 @@
26785
26966
  }));
26786
26967
  function GoogleButton(props) {
26787
26968
  const { classes } = useStyles$d();
26788
- const { disabled } = props;
26789
- 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 }));
26790
26971
  }
26791
26972
  function MicrosoftButton(props) {
26792
26973
  const { classes } = useStyles$d();
26793
- const { disabled } = props;
26794
- 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 }));
26795
26976
  }
26796
26977
  function GuestButton(props) {
26797
26978
  const { classes } = useStyles$d();
26798
- 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 }));
26799
26986
  }
26800
26987
 
26801
26988
  const useStyles$c = makeStyles()((theme) => ({
@@ -26807,7 +26994,7 @@
26807
26994
  const AuthTypeSelector = ({ baseURL, handleClose, name, }) => {
26808
26995
  const { classes } = useStyles$c();
26809
26996
  const [errorMessage, setErrorMessage] = React.useState('');
26810
- const [loginTypes, setLoginTypes] = React.useState([]);
26997
+ const [loginTypes, setLoginTypes] = React.useState(null);
26811
26998
  React.useEffect(() => {
26812
26999
  const controller = new AbortController();
26813
27000
  const { signal } = controller;
@@ -26821,6 +27008,9 @@
26821
27008
  }
26822
27009
  const data = (await response.json());
26823
27010
  setLoginTypes(data);
27011
+ if (data.length === 0) {
27012
+ setErrorMessage('No login types configured');
27013
+ }
26824
27014
  }
26825
27015
  getAuthTypes().catch((error) => {
26826
27016
  if (!isAbortException(error)) {
@@ -26832,26 +27022,35 @@
26832
27022
  };
26833
27023
  }, [baseURL]);
26834
27024
  function handleClick(authType) {
26835
- if (authType === 'google') {
26836
- handleClose('google');
26837
- }
26838
- else if (authType === 'microsoft') {
26839
- handleClose('microsoft');
26840
- }
26841
- else {
26842
- handleClose('guest');
26843
- }
26844
- }
26845
- const allowGoogle = loginTypes.includes('google');
26846
- const allowMicrosoft = loginTypes.includes('microsoft');
26847
- const allowGuest = loginTypes.includes('guest');
26848
- 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: () => {
26849
- handleClick('google');
26850
- } })) : null, allowMicrosoft ? (jsxRuntime.jsx(MicrosoftButton, { disabled: !allowMicrosoft, onClick: () => {
26851
- handleClick('microsoft');
26852
- } })) : null, allowGuest ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(material.Divider, { className: classes.divider }), jsxRuntime.jsx(GuestButton, { onClick: () => {
26853
- handleClick('guest');
26854
- } })] })) : 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: () => {
26855
27054
  handleClose();
26856
27055
  }, children: "Cancel" }) }), errorMessage ? (jsxRuntime.jsx(material.DialogContent, { children: jsxRuntime.jsx(material.DialogContentText, { color: "error", children: errorMessage }) })) : null] }));
26857
27056
  };
@@ -27047,13 +27246,13 @@
27047
27246
  },
27048
27247
  ]);
27049
27248
  });
27050
- if (authType !== 'guest') {
27249
+ if (authType.needsPopup) {
27051
27250
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
27052
- self.openAuthWindow(authType, resolve, reject);
27251
+ self.openAuthWindow(authType.name, resolve, reject);
27053
27252
  return;
27054
27253
  }
27055
27254
  const url = new URL('auth/login', baseURL);
27056
- const searchParams = new URLSearchParams({ type: authType });
27255
+ const searchParams = new URLSearchParams({ type: authType.name });
27057
27256
  url.search = searchParams.toString();
27058
27257
  const uri = url.toString();
27059
27258
  const response = await fetch(uri, { signal: self.controller.signal });
@@ -27174,7 +27373,7 @@
27174
27373
  socket.on('COMMON', (message) => {
27175
27374
  if ('checkResult' in message) {
27176
27375
  if (message.deleted) {
27177
- deleteCheckResult(message.checkResult._id.toString());
27376
+ deleteCheckResult(message.checkResult._id);
27178
27377
  }
27179
27378
  else {
27180
27379
  addCheckResult(message.checkResult);
@@ -28001,7 +28200,9 @@
28001
28200
  return newValues;
28002
28201
  });
28003
28202
  }
28004
- 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) => {
28005
28206
  updateValue(idx, editedValue);
28006
28207
  }, variant: "outlined", fullWidth: true }), jsxRuntime.jsx(material.IconButton, { "aria-label": "delete", size: "medium", edge: "end", onClick: () => {
28007
28208
  deleteValue(idx);
@@ -28013,7 +28214,9 @@
28013
28214
  });
28014
28215
 
28015
28216
  function DefaultAttributeViewer({ values }) {
28016
- 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}`))) }));
28017
28220
  }
28018
28221
 
28019
28222
  const useStyles$a = makeStyles()((theme) => ({
@@ -28319,6 +28522,7 @@
28319
28522
  const { classes } = useStyles$8();
28320
28523
  const [panelState, setPanelState] = React.useState(['attributes']);
28321
28524
  React.useEffect(() => {
28525
+ // eslint-disable-next-line @eslint-react/set-state-in-effect
28322
28526
  setPanelState(['attributes']);
28323
28527
  }, [feature]);
28324
28528
  if (!(feature && currentAssembly)) {
@@ -28405,6 +28609,7 @@
28405
28609
  */
28406
28610
  get minWithChildren() {
28407
28611
  var min = self.min;
28612
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
28408
28613
  var children = self.children;
28409
28614
  if (!children) {
28410
28615
  return min;
@@ -28431,6 +28636,7 @@
28431
28636
  */
28432
28637
  get maxWithChildren() {
28433
28638
  var max = self.max;
28639
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
28434
28640
  var children = self.children;
28435
28641
  if (!children) {
28436
28642
  return max;
@@ -28539,6 +28745,7 @@
28539
28745
  if (!featureTypeOntology.isTypeOf(self.type, 'transcript') && !featureTypeOntology.isTypeOf(self.type, 'pseudogenic_transcript')) {
28540
28746
  throw new Error('Only features of type "transcript" or equivalent can calculate CDS locations');
28541
28747
  }
28748
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
28542
28749
  var children = self.children;
28543
28750
  if (!children) {
28544
28751
  throw new Error('no CDS or exons in transcript');
@@ -29443,7 +29650,7 @@
29443
29650
  else {
29444
29651
  setSequenceOptions(defaultSequenceOptions);
29445
29652
  }
29446
- // eslint-disable-next-line react-hooks/exhaustive-deps
29653
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
29447
29654
  }, [feature]);
29448
29655
  if (!(currentAssembly && refData)) {
29449
29656
  return null;
@@ -29506,6 +29713,10 @@
29506
29713
  .join(';'), "(strand=", feature.strand === 1 ? '+' : '-', ";length=", getSequenceLength(sequenceSegments), ")", jsxRuntime.jsx("br", {}), wrapSequence(sequenceSegments, SEQUENCE_WRAP_LENGTH)] })] }));
29507
29714
  });
29508
29715
 
29716
+ var RemoveIcon = /*#__PURE__*/utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
29717
+ d: "M19 13H5v-2h14z"
29718
+ }), 'Remove');
29719
+
29509
29720
  var ContentCopyIcon = /*#__PURE__*/utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
29510
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"
29511
29722
  }), 'ContentCopy');
@@ -29514,23 +29725,6 @@
29514
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"
29515
29726
  }), 'ContentCut');
29516
29727
 
29517
- var RemoveIcon = /*#__PURE__*/utils.createSvgIcon(/*#__PURE__*/jsxRuntime.jsx("path", {
29518
- d: "M19 13H5v-2h14z"
29519
- }), 'Remove');
29520
-
29521
- const StyledTextField = newStyled(NumberTextField)(() => ({
29522
- '&.MuiFormControl-root': {
29523
- marginTop: 0,
29524
- marginBottom: 0,
29525
- width: '100%',
29526
- },
29527
- '& .MuiInputBase-input': {
29528
- fontSize: 12,
29529
- height: 20,
29530
- padding: 1,
29531
- paddingLeft: 10,
29532
- },
29533
- }));
29534
29728
  const SequenceContainer = newStyled('div')({
29535
29729
  display: 'flex',
29536
29730
  justifyContent: 'center',
@@ -29552,6 +29746,187 @@
29552
29746
  maxHeight: 30,
29553
29747
  },
29554
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
+ }));
29555
29930
  const Strand = (props) => {
29556
29931
  const { strand } = props;
29557
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" })) }));
@@ -29570,7 +29945,6 @@
29570
29945
  const currentAssembly = session.apolloDataStore.assemblies.get(assembly);
29571
29946
  const refData = currentAssembly?.getByRefName(refName);
29572
29947
  const { changeManager } = session.apolloDataStore;
29573
- const seqRef = React.useRef(null);
29574
29948
  const { changeInProgress } = session;
29575
29949
  if (!refData) {
29576
29950
  return null;
@@ -29997,184 +30371,6 @@
29997
30371
  },
29998
30372
  ];
29999
30373
  };
30000
- const getTranslationSequence = () => {
30001
- let wholeSequence = '';
30002
- const [firstLocation] = cdsLocations;
30003
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
30004
- for (const loc of sortedCDSLocations) {
30005
- wholeSequence += refData.getSequence(loc.min, loc.max);
30006
- }
30007
- if (strand === -1) {
30008
- // Original: ACGCAT
30009
- // Complement: TGCGTA
30010
- // Reverse complement: ATGCGT
30011
- wholeSequence = util$1.revcom(wholeSequence);
30012
- }
30013
- const elements = [];
30014
- for (let codonGenomicPos = 0; codonGenomicPos < wholeSequence.length; codonGenomicPos += 3) {
30015
- const codonSeq = wholeSequence
30016
- .slice(codonGenomicPos, codonGenomicPos + 3)
30017
- .toUpperCase();
30018
- const protein = util$1.defaultCodonTable[codonSeq] || '&';
30019
- // highlight start codon and stop codons
30020
- if (codonSeq === 'ATG') {
30021
- elements.push(jsxRuntime.jsx(material.Typography, { component: 'span', style: {
30022
- backgroundColor: changeInProgress ? 'lightgray' : 'yellow',
30023
- cursor: 'pointer',
30024
- border: '1px solid black',
30025
- }, onClick: () => {
30026
- if (changeInProgress) {
30027
- return;
30028
- }
30029
- // NOTE: codonGenomicPos is important here for calculating the genomic location
30030
- // of the start codon. We are using the codonGenomicPos as the key in the typography
30031
- // elements to maintain the genomic postion of the codon start
30032
- const startCodonGenomicLocation = getCodonGenomicLocation(codonGenomicPos);
30033
- if (startCodonGenomicLocation !== cdsMin && strand === 1) {
30034
- updateCDSLocation(cdsMin, startCodonGenomicLocation, feature, true);
30035
- }
30036
- if (startCodonGenomicLocation !== cdsMax && strand === -1) {
30037
- updateCDSLocation(cdsMax, startCodonGenomicLocation, feature, false);
30038
- }
30039
- }, children: protein }, codonGenomicPos));
30040
- }
30041
- else if (['TAA', 'TAG', 'TGA'].includes(codonSeq)) {
30042
- elements.push(jsxRuntime.jsx(material.Typography, { style: { backgroundColor: 'red', color: 'white' }, component: 'span', children: protein }, codonGenomicPos));
30043
- }
30044
- else {
30045
- elements.push(
30046
- // Pass the codonGenomicPos as the key to maintain the genomic position of the codon
30047
- jsxRuntime.jsx(material.Typography, { component: 'span', children: protein }, codonGenomicPos));
30048
- }
30049
- }
30050
- return elements;
30051
- };
30052
- // Codon position is the index of the start codon in the CDS genomic sequence
30053
- // Calculate the genomic location of the start codon based on the codon position in the CDS
30054
- const getCodonGenomicLocation = (codonGenomicPosition) => {
30055
- const [firstLocation] = cdsLocations;
30056
- let cdsLen = 0;
30057
- const sortedCDSLocations = firstLocation.toSorted(({ min: a }, { min: b }) => a - b);
30058
- // Suppose CDS locations are [{min: 0, max: 10}, {min: 20, max: 30}, {min: 40, max: 50}]
30059
- // and codonGenomicPosition is 25
30060
- // ((10 - 0) + (30 - 20) + (50 - 40)) > 25
30061
- // So, start codon is in (40, 50)
30062
- // 40 + (25-20) = 45 is the genomic location of the start codon
30063
- if (strand === 1) {
30064
- for (const loc of sortedCDSLocations) {
30065
- const locLength = loc.max - loc.min;
30066
- if (cdsLen + locLength > codonGenomicPosition) {
30067
- return loc.min + (codonGenomicPosition - cdsLen);
30068
- }
30069
- cdsLen += locLength;
30070
- }
30071
- }
30072
- else if (strand === -1) {
30073
- for (let i = sortedCDSLocations.length - 1; i >= 0; i--) {
30074
- const loc = sortedCDSLocations[i];
30075
- const locLength = loc.max - loc.min;
30076
- if (cdsLen + locLength > codonGenomicPosition) {
30077
- return loc.max - (codonGenomicPosition - cdsLen);
30078
- }
30079
- cdsLen += locLength;
30080
- }
30081
- }
30082
- if (strand === 1) {
30083
- return cdsMin;
30084
- }
30085
- return cdsMax;
30086
- };
30087
- const trimTranslationSequence = () => {
30088
- const sequenceElements = getTranslationSequence();
30089
- const translationSequence = sequenceElements
30090
- .map((el) => el.props.children)
30091
- .join('');
30092
- if (translationSequence.startsWith('M') &&
30093
- translationSequence.endsWith('*')) {
30094
- return;
30095
- }
30096
- // NOTE: We are maintaining the genomic location of the codon start as the "key"
30097
- // in typography elements. See getTranslationSequence function
30098
- const translSeqCodonStartGenomicPosArr = [];
30099
- for (const el of sequenceElements) {
30100
- translSeqCodonStartGenomicPosArr.push({
30101
- codonGenomicPos: el.key,
30102
- sequenceLetter: el.props.children,
30103
- });
30104
- }
30105
- if (translSeqCodonStartGenomicPosArr.length === 0) {
30106
- return;
30107
- }
30108
- // Trim any sequence before first start codon and after stop codon
30109
- const startCodonIndex = translationSequence.indexOf('M');
30110
- const stopCodonIndex = translationSequence.indexOf('*');
30111
- const startCodonPos = translSeqCodonStartGenomicPosArr[startCodonIndex].codonGenomicPos;
30112
- const stopCodonPos = translSeqCodonStartGenomicPosArr[stopCodonIndex].codonGenomicPos;
30113
- if (!startCodonPos || !stopCodonPos) {
30114
- return;
30115
- }
30116
- const startCodonGenomicLoc = getCodonGenomicLocation(startCodonPos);
30117
- let stopCodonGenomicLoc = getCodonGenomicLocation(stopCodonPos);
30118
- if (strand === 1) {
30119
- if (startCodonGenomicLoc > stopCodonGenomicLoc) {
30120
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
30121
- return;
30122
- }
30123
- let promise;
30124
- stopCodonGenomicLoc += 3; // move to end of stop codon
30125
- if (startCodonGenomicLoc !== cdsMin) {
30126
- promise = new Promise((resolve) => {
30127
- updateCDSLocation(cdsMin, startCodonGenomicLoc, feature, true, () => {
30128
- resolve(true);
30129
- });
30130
- });
30131
- }
30132
- if (stopCodonGenomicLoc !== cdsMax) {
30133
- if (promise) {
30134
- void promise.then(() => {
30135
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
30136
- });
30137
- }
30138
- else {
30139
- updateCDSLocation(cdsMax, stopCodonGenomicLoc, feature, false);
30140
- }
30141
- }
30142
- }
30143
- if (strand === -1) {
30144
- // reverse strand
30145
- if (startCodonGenomicLoc < stopCodonGenomicLoc) {
30146
- notify('Start codon genomic location should be less than stop codon genomic location', 'error');
30147
- return;
30148
- }
30149
- let promise;
30150
- stopCodonGenomicLoc -= 3; // move to end of stop codon
30151
- if (startCodonGenomicLoc !== cdsMax) {
30152
- promise = new Promise((resolve) => {
30153
- updateCDSLocation(cdsMax, startCodonGenomicLoc, feature, false, () => {
30154
- resolve(true);
30155
- });
30156
- });
30157
- }
30158
- if (stopCodonGenomicLoc !== cdsMin) {
30159
- if (promise) {
30160
- void promise.then(() => {
30161
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
30162
- });
30163
- }
30164
- else {
30165
- updateCDSLocation(cdsMin, stopCodonGenomicLoc, feature, true);
30166
- }
30167
- }
30168
- }
30169
- notify('Translation sequence trimmed to start and stop codons', 'success');
30170
- };
30171
- const onCopyClick = () => {
30172
- const seqDiv = seqRef.current;
30173
- if (!seqDiv) {
30174
- return;
30175
- }
30176
- void copyToClipboard(seqDiv);
30177
- };
30178
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) => {
30179
30375
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
30180
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) => {
@@ -30184,8 +30380,12 @@
30184
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) => {
30185
30381
  return updateCDSLocation(cdsMin, newLocation - 1, feature, true);
30186
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) => {
30187
- 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 &&
30188
- 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) => {
30189
30389
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
30190
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) => {
30191
30391
  return handleExonLocationChange(loc.max, newLocation, feature, false);
@@ -30194,14 +30394,10 @@
30194
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) => {
30195
30395
  return handleExonLocationChange(loc.min, newLocation - 1, feature, true);
30196
30396
  }, disabled: changeInProgress }) })), jsxRuntime.jsx(material.Grid, { size: 1, children: index !== transcriptExonParts.length - 1 &&
30197
- getThreePrimeSpliceSite(loc, index).map((site, idx) => (jsxRuntime.jsx(material.Typography, { component: 'span', color: site.color, children: site.spliceSite }, idx))) })] })) }, index));
30198
- })] }), 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: {
30199
- marginTop: 10,
30200
- display: 'flex',
30201
- flexDirection: 'row',
30202
- alignItems: 'center',
30203
- gap: 10,
30204
- }, 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 }))] }));
30205
30401
  });
30206
30402
 
30207
30403
  const HeaderTableCell = newStyled(material.TableCell)(() => ({
@@ -30238,7 +30434,7 @@
30238
30434
  const { assembly, feature, refName } = model;
30239
30435
  React.useEffect(() => {
30240
30436
  setPanelState(DEFAULT_PANELS);
30241
- // eslint-disable-next-line react-hooks/exhaustive-deps
30437
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
30242
30438
  }, [feature]);
30243
30439
  const session = util$1.getSession(model);
30244
30440
  const { pluginManager } = util$1.getEnv(session);
@@ -30297,8 +30493,8 @@
30297
30493
  typeName: 'TypeChange',
30298
30494
  changedIds: [featureId],
30299
30495
  featureId,
30300
- oldType: String(oldType),
30301
- newType: String(newType),
30496
+ oldType,
30497
+ newType,
30302
30498
  assembly: feature.assemblyId,
30303
30499
  });
30304
30500
  return changeManager.submit(change);
@@ -30394,7 +30590,7 @@
30394
30590
  if (initialValue !== value) {
30395
30591
  setValue(initialValue);
30396
30592
  }
30397
- // eslint-disable-next-line react-hooks/exhaustive-deps
30593
+ // eslint-disable-next-line @eslint-react/exhaustive-deps
30398
30594
  }, [initialValue]);
30399
30595
  React.useEffect(() => {
30400
30596
  if (blur) {
@@ -31243,8 +31439,9 @@
31243
31439
  const [top, left, width] = getFeatureBox(display, gene, row, block);
31244
31440
  const height = getRowCount$2(display, gene) * apolloRowHeight;
31245
31441
  if (width > 2) {
31246
- let selectedColor = configuration.readConfObject(session.getPluginConfiguration(), 'geneBackgroundColor', { featureType: gene.type });
31247
- 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);
31248
31445
  ctx.fillStyle = selectedColor;
31249
31446
  ctx.fillRect(left, top, width, height);
31250
31447
  }
@@ -31517,7 +31714,7 @@
31517
31714
  }
31518
31715
  return layout;
31519
31716
  }
31520
- function getContextMenuItems$1(display, feature) {
31717
+ function getContextMenuItems$1(display, transcript) {
31521
31718
  const { changeManager, regions, selectedFeature, session } = display;
31522
31719
  const [region] = regions;
31523
31720
  const currentAssemblyId = display.getAssemblyId(region.assemblyName);
@@ -31527,7 +31724,7 @@
31527
31724
  label: 'Open transcript editor',
31528
31725
  onClick: () => {
31529
31726
  const apolloTranscriptWidget = session.addWidget('ApolloTranscriptDetails', 'apolloTranscriptDetails', {
31530
- feature,
31727
+ feature: transcript,
31531
31728
  assembly: currentAssemblyId,
31532
31729
  changeManager,
31533
31730
  refName: region.refName,
@@ -31547,7 +31744,7 @@
31547
31744
  doneCallback();
31548
31745
  },
31549
31746
  changeManager,
31550
- sourceFeature: feature,
31747
+ sourceFeature: transcript,
31551
31748
  sourceAssemblyId: currentAssemblyId,
31552
31749
  selectedFeature,
31553
31750
  setSelectedFeature: (feature) => {
@@ -31567,7 +31764,7 @@
31567
31764
  doneCallback();
31568
31765
  },
31569
31766
  changeManager,
31570
- sourceFeature: feature,
31767
+ sourceFeature: transcript,
31571
31768
  sourceAssemblyId: currentAssemblyId,
31572
31769
  selectedFeature,
31573
31770
  setSelectedFeature: (feature) => {
@@ -31577,6 +31774,31 @@
31577
31774
  ]);
31578
31775
  },
31579
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
+ }
31580
31802
  return menuItems;
31581
31803
  }
31582
31804
  // False positive here, none of these functions use "this"
@@ -31763,7 +31985,7 @@
31763
31985
  letterUpper === 'C' ||
31764
31986
  letterUpper === 'G' ||
31765
31987
  letterUpper === 'T') {
31766
- return theme.palette.bases[letterUpper].main.toString();
31988
+ return theme.palette.bases[letterUpper].main;
31767
31989
  }
31768
31990
  return 'lightgray';
31769
31991
  }
@@ -31860,8 +32082,7 @@
31860
32082
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
31861
32083
  },
31862
32084
  get changeManager() {
31863
- return self.session.apolloDataStore
31864
- .changeManager;
32085
+ return self.session.apolloDataStore.changeManager;
31865
32086
  },
31866
32087
  getAssemblyId(assemblyName) {
31867
32088
  const { assemblyManager } = self.session;
@@ -31872,12 +32093,10 @@
31872
32093
  return assembly.name;
31873
32094
  },
31874
32095
  get selectedFeature() {
31875
- return self.session
31876
- .apolloSelectedFeature;
32096
+ return self.session.apolloSelectedFeature;
31877
32097
  },
31878
32098
  get hoveredFeature() {
31879
- return self.session
31880
- .apolloHoveredFeature;
32099
+ return self.session.apolloHoveredFeature;
31881
32100
  },
31882
32101
  }))
31883
32102
  .actions((self) => ({
@@ -31922,8 +32141,11 @@
31922
32141
  const { filteredFeatureTypes, trackMenuItems: superTrackMenuItems } = self;
31923
32142
  return {
31924
32143
  trackMenuItems() {
31925
- const { graphical, table, showCheckResults } = self;
31926
- 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 = [
31927
32149
  ...superTrackMenuItems(),
31928
32150
  {
31929
32151
  type: 'subMenu',
@@ -31975,7 +32197,7 @@
31975
32197
  {
31976
32198
  label: 'Filter features by type',
31977
32199
  onClick: () => {
31978
- const session = self.session;
32200
+ const { session } = self;
31979
32201
  self.session.queueDialog((doneCallback) => [
31980
32202
  FilterFeatures,
31981
32203
  {
@@ -31991,76 +32213,93 @@
31991
32213
  ]);
31992
32214
  },
31993
32215
  },
31994
- {
31995
- label: 'Export annotations',
31996
- icon: Export,
32216
+ ];
32217
+ if (backendDriver instanceof LocalDriver) {
32218
+ items.push({
32219
+ label: 'Import annotations',
32220
+ icon: InputIcon,
31997
32221
  onClick: () => {
31998
- const [region] = self.regions;
31999
- const { assemblyName } = region;
32000
- const assembly = self.getAssemblyId(assemblyName);
32001
- if (!assembly) {
32002
- return;
32003
- }
32004
- const session = self.session;
32005
32222
  session.queueDialog((doneCallback) => [
32006
- DownloadGFF3,
32223
+ ImportAnnotations,
32007
32224
  {
32008
32225
  session,
32226
+ assemblyName,
32009
32227
  handleClose: () => {
32010
32228
  doneCallback();
32011
32229
  },
32012
- assembly,
32013
32230
  },
32014
32231
  ]);
32015
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
+ ]);
32016
32256
  },
32017
- {
32018
- label: 'View Change Log',
32019
- icon: TrackChangesIcon,
32020
- onClick: () => {
32021
- const [region] = self.regions;
32022
- const { assemblyName } = region;
32023
- const assembly = self.getAssemblyId(assemblyName);
32024
- if (!assembly) {
32025
- return;
32026
- }
32027
- const session = self.session;
32028
- session.queueDialog((doneCallback) => [
32029
- ViewChangeLog,
32030
- {
32031
- session,
32032
- handleClose: () => {
32033
- doneCallback();
32034
- },
32035
- 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();
32036
32274
  },
32037
- ]);
32038
- },
32275
+ assembly,
32276
+ },
32277
+ ]);
32039
32278
  },
32040
- {
32041
- label: 'View Check Results',
32042
- icon: FactCheckIcon,
32043
- onClick: () => {
32044
- const [region] = self.regions;
32045
- const { assemblyName } = region;
32046
- const assembly = self.getAssemblyId(assemblyName);
32047
- if (!assembly) {
32048
- return;
32049
- }
32050
- const session = self.session;
32051
- session.queueDialog((doneCallback) => [
32052
- ViewCheckResults,
32053
- {
32054
- session,
32055
- handleClose: () => {
32056
- doneCallback();
32057
- },
32058
- 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();
32059
32296
  },
32060
- ]);
32061
- },
32297
+ assembly,
32298
+ },
32299
+ ]);
32062
32300
  },
32063
- ];
32301
+ });
32302
+ return items;
32064
32303
  },
32065
32304
  };
32066
32305
  })
@@ -33126,8 +33365,7 @@
33126
33365
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
33127
33366
  },
33128
33367
  get changeManager() {
33129
- return self.session.apolloDataStore
33130
- .changeManager;
33368
+ return self.session.apolloDataStore.changeManager;
33131
33369
  },
33132
33370
  getAssemblyId(assemblyName) {
33133
33371
  const { assemblyManager } = self.session;
@@ -33138,12 +33376,10 @@
33138
33376
  return assembly.name;
33139
33377
  },
33140
33378
  get selectedFeature() {
33141
- return self.session
33142
- .apolloSelectedFeature;
33379
+ return self.session.apolloSelectedFeature;
33143
33380
  },
33144
33381
  get hoveredFeature() {
33145
- return self.session
33146
- .apolloHoveredFeature;
33382
+ return self.session.apolloHoveredFeature;
33147
33383
  },
33148
33384
  get height() {
33149
33385
  const { sequenceRowHeight } = self;
@@ -33355,11 +33591,9 @@
33355
33591
  onUpdate(newForms);
33356
33592
  setExcludedTranscripts(newForms);
33357
33593
  };
33358
- 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) => (
33359
- // eslint-disable-next-line react/jsx-key
33360
- 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: () => {
33361
33595
  handleChange(item);
33362
- }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }))) }) }) })] }) }));
33596
+ }, slotProps: { input: { 'aria-label': 'controlled' } } }), label: item }, item))) }) }) })] }) }));
33363
33597
  });
33364
33598
 
33365
33599
  let forwardFillLight = null;
@@ -34205,8 +34439,7 @@
34205
34439
  return internetAccounts.find((ia) => configuration.getConf(ia, 'internetAccountId') === internetAccountConfigId);
34206
34440
  },
34207
34441
  get changeManager() {
34208
- return self.session.apolloDataStore
34209
- .changeManager;
34442
+ return self.session.apolloDataStore.changeManager;
34210
34443
  },
34211
34444
  getAssemblyId(assemblyName) {
34212
34445
  const { assemblyManager } = self.session;
@@ -34217,12 +34450,10 @@
34217
34450
  return assembly.name;
34218
34451
  },
34219
34452
  get selectedFeature() {
34220
- return self.session
34221
- .apolloSelectedFeature;
34453
+ return self.session.apolloSelectedFeature;
34222
34454
  },
34223
34455
  get hoveredFeature() {
34224
- return self.session
34225
- .apolloHoveredFeature;
34456
+ return self.session.apolloHoveredFeature;
34226
34457
  },
34227
34458
  }))
34228
34459
  .actions((self) => ({
@@ -34350,7 +34581,7 @@
34350
34581
  {
34351
34582
  label: 'Filter features by type',
34352
34583
  onClick: () => {
34353
- const session = self.session;
34584
+ const { session } = self;
34354
34585
  self.session.queueDialog((doneCallback) => [
34355
34586
  FilterFeatures,
34356
34587
  {
@@ -34788,17 +35019,12 @@
34788
35019
  if (!featureTypeOntology) {
34789
35020
  throw new Error('featureTypeOntology is undefined');
34790
35021
  }
34791
- let foundFeature;
34792
- if (self.geneTrackRowNums.includes(row)) {
34793
- foundFeature = layoutRow.find((f) => f.feature.type == 'exon' &&
35022
+ const foundFeature = self.geneTrackRowNums.includes(row)
35023
+ ? layoutRow.find((f) => f.feature.type == 'exon' &&
34794
35024
  bp >= f.feature.min &&
34795
- bp <= f.feature.max);
34796
- if (!foundFeature) {
34797
- foundFeature = layoutRow.find((f) => bp >= f.feature.min && bp <= f.feature.max);
34798
- }
34799
- }
34800
- else {
34801
- 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) => {
34802
35028
  const { feature } = f;
34803
35029
  const featureID = feature.attributes.get('gff_id')?.toString();
34804
35030
  const isTranscript = featureTypeOntology.isTypeOf(feature.type, 'transcript');
@@ -34820,7 +35046,6 @@
34820
35046
  }
34821
35047
  return false;
34822
35048
  });
34823
- }
34824
35049
  if (!foundFeature) {
34825
35050
  return mousePosition;
34826
35051
  }
@@ -35144,26 +35369,32 @@
35144
35369
  const apolloAssembly = apolloSessionModel.apolloDataStore.assemblies.get(assembly.name);
35145
35370
  const refSeq = apolloAssembly?.refSeqs.get(refSeqId);
35146
35371
  const features = refSeq?.getFeatures(region.start, region.end);
35147
- const getDestinationFeatures = () => {
35148
- const filteredFeatures = [];
35149
- for (const f of features ?? []) {
35150
- if (f.min > region.end || f.max < region.start) {
35151
- continue;
35152
- }
35153
- // Destination feature should be of type gene
35154
- if (featureTypeOntology?.isTypeOf(f.type, 'gene')) {
35155
- const featureSnapshot = mobxStateTree.getSnapshot(f);
35156
- filteredFeatures.push(featureSnapshot);
35157
- }
35158
- }
35159
- return filteredFeatures;
35160
- };
35161
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
+ };
35162
35387
  setErrorMessage('');
35163
- const features = getDestinationFeatures();
35164
- setDestinationFeatures(features);
35165
- setSelectedDestinationFeature(features[0]);
35166
- }, [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
+ ]);
35167
35398
  const handleParentFeatureCheck = (event) => {
35168
35399
  const isChecked = event.target.checked;
35169
35400
  setParentFeatureChecked(isChecked);
@@ -35443,8 +35674,21 @@
35443
35674
  getAnnotationFeature(jbrowseFeature, refSeqId) {
35444
35675
  const start = jbrowseFeature.get('start');
35445
35676
  const end = jbrowseFeature.get('end');
35446
- const strand = jbrowseFeature.get('strand');
35447
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');
35448
35692
  const cigarData = jbrowseFeature.get('CIGAR');
35449
35693
  const ops = parseCigar(cigarData);
35450
35694
  let position = start;
@@ -35460,9 +35704,7 @@
35460
35704
  case 'M':
35461
35705
  case '=':
35462
35706
  case 'X': {
35463
- if (currentExonStart === undefined) {
35464
- currentExonStart = position;
35465
- }
35707
+ currentExonStart ??= position;
35466
35708
  position += len;
35467
35709
  break;
35468
35710
  }
@@ -35888,7 +36130,7 @@
35888
36130
  : undefined, menuItems: contextMenuItems })] }))] }));
35889
36131
  });
35890
36132
 
35891
- 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, }) {
35892
36134
  const { apolloRowHeight, highestRow, showFeatureLabels } = model;
35893
36135
  const featureLabelSpacer = showFeatureLabels ? 2 : 1;
35894
36136
  return (jsxRuntime.jsx("div", { style: {
@@ -35960,8 +36202,7 @@
35960
36202
  regionNumber: idx,
35961
36203
  })?.offsetPx ?? 0) - lgv.offsetPx;
35962
36204
  const [feature] = checkResult.featureIds;
35963
- if (!feature ||
35964
- !feature.parent ||
36205
+ if (!feature?.parent ||
35965
36206
  !looksLikeGene(feature.parent, session)) {
35966
36207
  return null;
35967
36208
  }
@@ -36229,18 +36470,15 @@
36229
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"
36230
36471
  }), 'Save');
36231
36472
 
36232
- /* eslint-disable @typescript-eslint/no-unsafe-assignment */
36233
36473
  /* eslint-disable @typescript-eslint/no-confusing-void-expression */
36234
36474
  const ApolloJobModel = mobxStateTree.types
36235
36475
  .model('JobsManager', {})
36236
36476
  .views((self) => ({
36237
36477
  get jobStatusWidget() {
36238
36478
  const { widgets } = util$1.getSession(self);
36239
- let jobStatusWidget = widgets.get('JobsList');
36240
- if (!jobStatusWidget) {
36479
+ const jobStatusWidget = widgets.get('JobsList') ??
36241
36480
  // @ts-expect-error: addWidget function not detected on the session
36242
- jobStatusWidget = util$1.getSession(self).addWidget('JobsListWidget', 'JobsList');
36243
- }
36481
+ util$1.getSession(self).addWidget('JobsListWidget', 'JobsList');
36244
36482
  return jobStatusWidget;
36245
36483
  },
36246
36484
  }))
@@ -36519,19 +36757,15 @@
36519
36757
  continue;
36520
36758
  }
36521
36759
  const { assemblyName, refName } = region;
36522
- let assembly = self.assemblies.get(assemblyName);
36523
- if (!assembly) {
36524
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
36525
- }
36760
+ const assembly = self.assemblies.get(assemblyName) ??
36761
+ self.assemblies.put({ _id: assemblyName, refSeqs: {} });
36526
36762
  const [firstFeature] = features;
36527
- let ref = assembly.refSeqs.get(firstFeature.refSeq);
36528
- if (!ref) {
36529
- ref = assembly.refSeqs.put({
36763
+ const ref = assembly.refSeqs.get(firstFeature.refSeq) ??
36764
+ assembly.refSeqs.put({
36530
36765
  _id: firstFeature.refSeq,
36531
36766
  name: refName,
36532
36767
  features: {},
36533
36768
  });
36534
- }
36535
36769
  for (const feature of features) {
36536
36770
  if (!ref.features.has(feature._id)) {
36537
36771
  ref.features.put(feature);
@@ -36548,24 +36782,153 @@
36548
36782
  }
36549
36783
  const { refSeq, seq } = yield backendDriver.getSequence(region);
36550
36784
  const { assemblyName, end, refName, start } = region;
36551
- let assembly = self.assemblies.get(assemblyName);
36552
- if (!assembly) {
36553
- assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} });
36554
- }
36555
- let ref = assembly.refSeqs.get(refSeq);
36556
- if (!ref) {
36557
- ref = assembly.refSeqs.put({
36558
- _id: refSeq,
36559
- name: refName,
36560
- sequence: [],
36561
- });
36562
- }
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: [] });
36563
36789
  ref.addSequence({ start, stop: end, sequence: seq });
36564
36790
  }
36565
36791
  }),
36566
36792
  }));
36567
36793
  }
36568
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
+
36569
36932
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
36570
36933
  /* eslint-disable @typescript-eslint/no-unsafe-assignment */
36571
36934
  function extendSession(pluginManager, sessionModel) {
@@ -36651,8 +37014,7 @@
36651
37014
  for (const block of dynamicBlocks.contentBlocks) {
36652
37015
  const { assemblyName, end, refName, start } = block;
36653
37016
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
36654
- if (assembly &&
36655
- assembly.backendDriverType === 'CollaborationServerDriver') {
37017
+ if (assembly?.backendDriverType === 'CollaborationServerDriver') {
36656
37018
  locations.push({ assemblyName, refName, start, end });
36657
37019
  }
36658
37020
  }
@@ -36711,8 +37073,8 @@
36711
37073
  for (const block of dynamicBlocks.contentBlocks) {
36712
37074
  const { assemblyName, end, refName, start } = block;
36713
37075
  const assembly = self.apolloDataStore.assemblies.get(assemblyName);
36714
- if (assembly &&
36715
- assembly.backendDriverType === 'CollaborationServerDriver') {
37076
+ if (assembly?.backendDriverType ===
37077
+ 'CollaborationServerDriver') {
36716
37078
  locations.push({ assemblyName, refName, start, end });
36717
37079
  }
36718
37080
  }
@@ -36832,6 +37194,37 @@
36832
37194
  beforeDestroy() {
36833
37195
  self.abortController.abort(new DOMException('Clean up Apollo session', 'AbortError'));
36834
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
+ },
36835
37228
  }))
36836
37229
  .views((self) => {
36837
37230
  const superTrackActions = self
@@ -36963,7 +37356,6 @@
36963
37356
  }
36964
37357
 
36965
37358
  /* eslint-disable @typescript-eslint/unbound-method */
36966
- /* eslint-disable @typescript-eslint/no-unnecessary-condition */
36967
37359
  /* eslint-disable @typescript-eslint/no-misused-promises */
36968
37360
  /* eslint-disable @typescript-eslint/no-explicit-any */
36969
37361
  function isApolloMessageData(data) {
@@ -37151,10 +37543,10 @@
37151
37543
  });
37152
37544
  if (!inWebWorker) {
37153
37545
  pluginManager.addToExtensionPoint('Core-extendWorker', (handle) => {
37154
- if (!('on' in handle && handle.on)) {
37546
+ if (!('on' in handle.client && handle.client.on)) {
37155
37547
  return handle;
37156
37548
  }
37157
- handle.on('apollo', async (event) => {
37549
+ handle.client.on('apollo', async (event) => {
37158
37550
  if (!isApolloMessageData(event)) {
37159
37551
  return;
37160
37552
  }
@@ -37172,7 +37564,7 @@
37172
37564
  break;
37173
37565
  }
37174
37566
  const { seq: sequence } = await backendDriver.getSequence(region);
37175
- handle.workers[0].postMessage({
37567
+ handle.worker.postMessage({
37176
37568
  apollo,
37177
37569
  messageId,
37178
37570
  sequence,
@@ -37190,7 +37582,7 @@
37190
37582
  break;
37191
37583
  }
37192
37584
  const regions = await backendDriver.getRegions(assembly);
37193
- handle.workers[0].postMessage({
37585
+ handle.worker.postMessage({
37194
37586
  apollo,
37195
37587
  messageId,
37196
37588
  regions,
@@ -37208,7 +37600,7 @@
37208
37600
  break;
37209
37601
  }
37210
37602
  const refNameAliases = await backendDriver.getRefNameAliases(assembly);
37211
- handle.workers[0].postMessage({
37603
+ handle.worker.postMessage({
37212
37604
  apollo,
37213
37605
  messageId,
37214
37606
  refNameAliases,